Banana Code
Banana Code

Reputation: 825

Is there any way to efficiently express the Javascript code like the chaining method in JQuery?

In jQuery, we can run multiple methods within a single statement in order to efficiently express the jQuery code:

$("#p1").css("color", "red").html("Hello world!").attr("class","democlass");

And how about Javascript?

document.getElementById("p1").style.color = "red";
document.getElementById("p1").innerHTML = "Hello world!";
document.getElementById("p1").setAttribute("class","democlass");

Upvotes: 0

Views: 86

Answers (4)

Emil S. Jørgensen
Emil S. Jørgensen

Reputation: 6366

If you want to write jQuery like chaining, the trick is to limit the amount of times the DOM is touched and control when to apply changes. Something like this will do that:

/**
 * Global variable for closed scope functions
 *
 * @param {HTMLElement} node
 * @returns myElementTreaterCls
 */
var notjQuery = (function() {
  var myElementTreaterCls = (function() {
    /**
     * Creates an instance of myElementTreaterCls.
     *
     * @param {HTMLElement} node
     *
     * @memberOf myElementTreaterCls
     */
    function myElementTreaterCls(node) {
      this.node = node;
      /**
       * Styling changes
       *
       * @type {string}
       * @memberOf myElementTreaterCls
       */
      this.currentCss = null;
    }
    /**
     * Applies all changes to the DOM element
     *
     * @returns myElementTreaterCls
     *
     * @memberOf myElementTreaterCls
     */
    myElementTreaterCls.prototype.render = function() {
      if (this.currentCss != null) {
        this.node.style.cssText = this.currentCss;
        this.currentCss = null;
      }
      return this;
    };
    /**
     * Add styling rules to the DOM element
     * If "render" is falsy the rules aren't applied, saving a paint
     *
     * @param {any} [rules={}]
     * @param {boolean} [render=true]
     * @returns myElementTreaterCls
     *
     * @memberOf myElementTreaterCls
     */
    myElementTreaterCls.prototype.addCss = function(rules, render) {
      if (rules === void 0) {
        rules = {};
      }
      if (render === void 0) {
        render = true;
      }
      if (this.currentCss === null) {
        this.currentCss = this.node.style.cssText.toString();
      }
      for (var cssRule in rules) {
        if (rules.hasOwnProperty(cssRule)) {
          var rule = rules[cssRule];
          this.currentCss += " " + cssRule + ": " + rule + ";";
        }
      }
      if (render === true) {
        this.render();
      }
      return this;
    };
    return myElementTreaterCls;
  }());
  //Returns Instantiate function to "notjQuery"
  return function myElementTreater(node) {
    return new myElementTreaterCls(node);
  };
})();

And here is a use example:

//Minified version of the "notjQuery" code:
var notjQuery=function(){var t=function(){function t(t){this.node=t,this.currentCss=null}return t.prototype.render=function(){return null!=this.currentCss&&(this.node.style.cssText=this.currentCss,this.currentCss=null),this},t.prototype.addCss=function(t,r){void 0===t&&(t={}),void 0===r&&(r=!0),null===this.currentCss&&(this.currentCss=this.node.style.cssText.toString());for(var n in t)if(t.hasOwnProperty(n)){var s=t[n];this.currentCss+=" "+n+": "+s+";"}return r===!0&&this.render(),this},t}();return function(r){return new t(r)}}();


//>>TEST<<//
//Setup node
var n = document.createElement("p");
n.innerHTML = "TEST PARAGRAPH";
n.style.color = "red";
document.body.appendChild(n);
//Instantiate, make change but delay applying them
var el = notjQuery(n)
  .addCss({
    "float": "right",
    right: "20px",
    color: "green"
  }, false);
//Make changes many times in a loop, which should cause a lot of repaints, but doesn't because we delay applying them
var i = 0,
  interval;
interval = setInterval(function() {
  i++;
  if (i < 100) {
    el.addCss({
      right: i + "px",
      display: (i % 2 ? "block" : "none")
    }, false);
  } else {
    //Stop loop
    clearInterval(interval);
    //Finally applying rules to node
    el.render();
  }
}, 1);

Of course this is just to prove the concept.

Be mindful of the DOM!

Upvotes: 0

IMSoP
IMSoP

Reputation: 97928

The performance gain is simply because you are only searching for the element once, and then storing it in a variable. jQuery makes this easy by returning that variable to you repeatedly, but with or without jQuery, you can just use an explicit variable for the same effect.

Without jQuery:

var p1 = document.getElementById("p1");
p1.style.color = "red";
p1.innerHTML = "Hello world!";
p1.setAttribute("class","democlass");

With jQuery:

var p1 = $("#p1");
p1.css("color", "red");
p1.html("Hello world!");
p1.attr("class","democlass");

Upvotes: 1

smarber
smarber

Reputation: 5084

The performance enhancement we should never forget is to look for the element in the DOM only once when possible:

var p1 = document.getElementById("p1");

p1.style.color = "red";
p1.innerHTML = "Hello world!";
p1.setAttribute("class","democlass");

Upvotes: 1

Rory McCrossan
Rory McCrossan

Reputation: 337646

Note that method chaining in jQuery make zero difference to performance. It's just syntactic sugar to make the code shorter and prettier.

That said you can make a marginal improvement to your JS code by storing the p1 element in a variable, and also using the classList object to add/remove classes, like this:

var p1 = document.getElementById("p1");
p1.style.color = "red";
p1.innerHTML = "Hello world!";
p1.classList.add('democlass');

Upvotes: 1

Related Questions