17

Is there a way to chain HTML5 classList API chaining?.

this will not work

var sidebar = document.querySelector("#sidebar");

sidebar.classList.toggle("active").classList.remove("hover")

while this will work

var sidebar = document.querySelector("#sidebar");

sidebar.classList.toggle("active"); 
sidebar.classList.remove("hover")

NOTE: NO jquery please

5
  • 1
    You could write your own function that works like jQuery. Commented Feb 22, 2015 at 2:18
  • 1
    jsfiddle.net/7f8ud2s0 Commented Feb 22, 2015 at 2:38
  • 2
    for fun: with(sidebar.classList){ toggle("active"); remove("hover"); } Commented Feb 22, 2015 at 3:53
  • @dandavis amazing that would have been the answer but Use of the with statement is not recommended, Commented Feb 22, 2015 at 6:55
  • If you try to chain toggle, you're going to lose its ability to report to you on the result of the toggling. Anyway, I'm still trying to figure out after these many years what is so incredibly wonderful about chaining. Commented Mar 19, 2015 at 11:15

3 Answers 3

29

You can create chainability via a little object with chainable methods:

function classList(elt) {
  var list = elt.classList;

  return {
      toggle: function(c) { list.toggle(c); return this; },
      add:    function(c) { list.add   (c); return this; },
      remove: function(c) { list.remove(c); return this; }
  };

}

Then you can chain to your heart's content:

classList(elt).remove('foo').add('bar').toggle('baz')
Sign up to request clarification or add additional context in comments.

1 Comment

So the answer is no.
4

For chaining to work, a method has to return the object that you want to chain with. Because .classList methods do not return either the classList object or the DOM object, you can't natively do chaining with them.

You could, of course write your own methods and have them return the appropriate object and thus re-implement the functionality in a chainable way, but you'd have to put them on a system prototype in order to be able to use them as easily.

Without reimplementing chainable methods, you could shorten your code a bit:

var sidebarList = document.querySelector("#sidebar").classList;

sidebarList.toggle("active"); 
sidebarList.remove("hover");

If you want to add chainable methods on the actual HTML5 objects, you could do this:

(function() {
    var p = HTMLElement.prototype;
    p.clAdd = function(cls) {
        this.classList.add(cls);
        return this;
    };

    p.clRemove = function(cls) {
        this.classList.remove(cls);
        return this;
    };

    p.clToggle = function(cls) {
        this.classList.toggle(cls);
        return this;
    }
})();

// sample use
document.querySelector("#sidebar").clAdd("active").clRemove("inactive");

Working demo: http://jsfiddle.net/jfriend00/t6w4aj0w/


Or, if you want a .classList type interface, you could do this:

Object.defineProperty(HTMLElement.prototype, "classListChain", {
    get: function() {
        var self = this;
        return {
            add: function(cls) {
                self.classList.add(cls);
                return self;
            },
            remove: function(cls) {
                self.classList.remove(cls);
                return self;
            },
            toggle: function(cls) {
                self.classList.toggle(cls);
                return self;
            }
        }
    }
});

// sample use
document.querySelector("#sidebar").classListChain.add("active").classListChain.remove("inactive");

Working demo: http://jsfiddle.net/jfriend00/pxm11vcq/


FYI, because both of these options chain the actual DOM element (unlike torazaburo's method which chains a custom object), you can add a DOM element method/property at the end as in:

el.classListChain.add("active").style.visibility = "visible";

Or, you can do something like this:

var el = document.querySelector("#sidebar").classListChain.add("active");

1 Comment

@Tambo - Your question asked for a "way to chain HTML5 classList API" which is not what the answer you accepted actually does. It makes a global function of its own creation that can chain - it doesn't do chaining of the actual HTML5 classList API. Apparently that is what you now want, but in fairness is not what you asked for. Anyway, I added two different ways to do chaining if you so desire.
0

If you only support modern browsers, chaining is one of the rare advantages jQuery still holds against vanilla JavaScript, but this shouldn't stop you. Like @jfriend00 suggested above, you can create your own add/remove/toggle methods that can be chained by simply returning the this object.

Here they are:

Element.prototype.addClassC = Element.prototype.addClassC || function (htmlClass) {
    this.classList.add(htmlClass);
    return this;
}
Element.prototype.removeClassC = Element.prototype.removeClassC || function (htmlClass) {
    this.classList.remove(htmlClass);
    return this;
}
Element.prototype.toggleClassC = Element.prototype.toggleClassC || function (htmlClass) {
    this.classList.toggle(htmlClass);
    return this;
}

In your case you would then apply them like so:

sidebar.toggleClassC('active').removeClassC('hover');

A note about naming. I would suggest against simply using addClass and removeClass because those could conflict with jQuery's own methods, or someone reading the code would mistake them for jQuery's methods. Here I added a C which stands for Chaining, but you can name it something more meaningful to you.

Edit: I added a check to make sure our methods were not already defined earlier in order to avoid overriding. I also switched the class variable name from cssClass to htmlClass because that's more correct (html has classes, css has selectors).

2 Comments

Just curious why you've decided to name the parameters "classes". It's a single class name, right? (Although some browsers do apparently support variadicity.)
The code could have been refactored to actually support multiple classes via an array, but then you're right, browser support for multiple classes is not 100% (IE being the usual culprit), so I corrected the code to avoid confusion. The functions can be chained, anyway. Thanks for noting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.