3

If you have a function which fires on a scroll event, which is better.

  1. Check if a class is already added and if not add it
  2. Don't do any checks but just add the class everytime needed.
$(document).on('scroll', function () {
   var scrollTop = $(this).scrollTop();
   if (scrollTop > 50) {
      if (!$("nav .branding").hasClass("collapse"))
         $("nav .branding").addClass("collapse");
   } else {
      if ($("nav .branding").hasClass("collapse"))
         $("nav .branding").removeClass("collapse");
   }
});

or

$(document).on('scroll', function () {
   var scrollTop = $(this).scrollTop();
   if (scrollTop > 50) {
      $("nav .branding").addClass("collapse");
   } else {
      $("nav .branding").removeClass("collapse");
   }
});

The first occasion there is an extra check but in the other action there might (?) be a more intense operation(?)

NLAnaconda
  • 1,534
  • 3
  • 14
  • 38
  • One thing you can do (if you care about performance) regardless is keep any selectors outside of the scroll event. Just make a variable storing the `.branding` element outside of the scroll event handler. – xec Aug 24 '15 at 12:13
  • @user2740744, if you are not satisfied with the resolution of this question and want a bit more discussion on it then see http://stackoverflow.com/questions/7403472/check-if-class-already-assigned-before-adding/32183320#32183320. I have addressed your question particularly in my answer there. – Igwe Kalu Aug 24 '15 at 13:20

5 Answers5

11

If you are concerned about performance there are a few things you can/should do:

1. Cache your selectors:

Dom interaction is expensive, and in your case you call $("nav .branding") multiple times.

Store it in a variable like so var $branding = $("nav .branding").

2. Use vanilla javascript to handle the class

Depending on browser support.

var branding = document.querySelector('nav .branding');

if (scrollTop > 50) {
  if (!branding.classList.contains("collapse")) {
    branding.classList.add("collapse");
  }
} else {
  if (branding.classList.contains("collapse")) {
    branding.classList.remove("collapse");
  }
}

Please keep in mind that not all browsers support the classList property, on the MDN you can find information about compatibility and also a polyfill.

Concerning your original question: jQuery's addClass has an inbuilt check whether the class already exists, so you're better to go when you dont use hasClass beforehand as it will cause redundancy, but do keep in mind that addClass is ~70% less performant than classList.

Patsy Issa
  • 11,113
  • 4
  • 55
  • 74
  • 2
    @Alex his answer is performance based. – Deblaton Jean-Philippe Aug 24 '15 at 12:28
  • @DeblatonJean-Philippe there is no need to test for performance when the check obviously is redundant – Alex Aug 24 '15 at 12:29
  • 2
    This is the best answer so far, as it adresses the only actual performance issue (the selcting running over and over in scroll event), and the vanilla version is of course preferred to any non-native api. @Alex has a point though: you should not have to check for existance of the class, the `classList` API is clever enough :) – xec Aug 24 '15 at 12:29
  • if this answer was about performance, the author would have pointed out that this action is fired with every pixel scrolled. a timeout can help out here. but this was not the origin of the question, it was if using an additional `hasClass` would be rational or not. – Alex Aug 24 '15 at 12:31
  • There is even a nice syntax to avoid the first if check: `branding.classList.toggle("collapse", scrollTop > 50);` – xec Aug 24 '15 at 12:31
  • 2
    @Alex feel free to contribute to the answer :) – Patsy Issa Aug 24 '15 at 12:32
  • I just re-read your first comment, do you even know what you are talking about? Anything that has to go through jquery will be slower than vanilla javascript because guess what, jquery is javascript with added clutter for convenience. – Patsy Issa Aug 24 '15 at 12:36
  • 1
    It's up for the op to decide whether or not he wants to support ie 8-9, don't throw libraries without knowing how/when to use them :) – Patsy Issa Aug 24 '15 at 12:43
  • 4
    As far as I am concerned the question is tagged with javascript, there are many ways to solve a problem and this is one of them, [have some stats](http://jsperf.com/hasclass-vs-classlist-dot-contains). This is getting too chatty. – Patsy Issa Aug 24 '15 at 12:47
  • 2
    @Alex If you find the answer lacking either add to it or make a more complete answer. – Patsy Issa Aug 25 '15 at 07:22
2

Like as you said, the .hasClass() is an extra check which takes up browser's memory. The .addClass() first checks internally and then adds only when the particular class is not there.

So, obviously, .addClass() and .removeClass() is more performant than using .hasClass() to first check. Basically, using .hasClass() is an extra useless effort.

This is a snippet to prove that .addClass() already checks for existing class or doesn't duplicate the class names:

$(function () {
  $("#classCheck").addClass("class");
  $("#classCheck").addClass("class");
});
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
<div id="classCheck"></div>

Please I would request everyone to consider the above snippet as the proof for .addClass() not duplicating the class names.

A simple check in the console would have told you that calling addClass multiple times with the same class is safe.

Specifically you can find the check in the source:

if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
  setClass += classNames[ c ] + " ";
}

Also, as Alex pointed out in comments, you can improve the performance if you store the jQuery selector objects. Instead of using $("nav .branding") multiple times, you can use: $nav_branding = $("nav .branding") and use the variable instead.

Community
  • 1
  • 1
Praveen Kumar Purushothaman
  • 164,888
  • 24
  • 203
  • 252
2

The second is preferred because calling addClass/removeClass depening on an existing class results in an unobtrusive consequence.

Meaning, if addClass is called and the class already exists, it will not be added again.

Same for removing. If the class does not exist, nothing is done

AmmarCSE
  • 30,079
  • 5
  • 45
  • 53
0

The first solution is faster, but not by much. (read: should be irrelevant) https://github.com/jquery/jquery/blob/master/src/attributes/classes.js

If you want smooth performance I would recommend throttling the function.

read: https://remysharp.com/2010/07/21/throttling-function-calls

Throttling ensures that a function is only called every n ms.

$(document).on('scroll', throttle(500, function () {
  var scrollTop = $(this).scrollTop();
  if (scrollTop > 50) {
    if (!$("nav .branding").hasClass("collapse")){
      $("nav .branding").addClass("collapse");
    }
  } else {
    if ($("nav .branding").hasClass("collapse")) {
      $("nav .branding").removeClass("collapse");
    }
  }
}));   

edit:

as @kitler mentioned storing a reference to your dom element is a good tip. this prevents jquery from having to find the element every time.

Andreas Møller
  • 839
  • 5
  • 9
-2

I'd go with option 2 as in option 1 you do a jQuery DOM selection just to test if the class is available, which should the expensive part of it all.

That being said, it's probably best to store the reference to the DOM object only once, outside your function, and work on that reference only.

connexo
  • 53,704
  • 14
  • 91
  • 128