kubetail-org / sentineljs

Detect new DOM nodes using CSS selectors (650 bytes)
MIT License
1.13k stars 51 forks source link

Some changes to reduce the code size #2

Closed chris-morgan closed 6 years ago

chris-morgan commented 6 years ago

Omit the name and time stamp from the gzip file with gzip --no-name to save a handful of bytes (thirteen).

Save another three bytes by removing the trailing semicolon and EOL.

This takes it down from 682 to 666 bytes.

I was then able to save another 77 bytes (to 589 bytes, >13%) before I decided that I was really spending too much time on this. Golfing gzipped minified JavaScript size seems to be a hobby of mine.

Do whatever you like (nothing or anything) with this JavaScript which should be functional but is completely untested:

sentinel = (function (document, isArray) {
var selectorToAnimationMap = {};
var animationCallbacks = {};
var styleEl;
var styleSheet;
var cssRules;

return this.sentinel || {
  on: function (cssSelectors, callback, extraAnimations) {
    if (!callback) return;

    // initialize animationstart event listener
    if (!styleEl) {
      // add animationstart event listener
      ['animationstart', 'mozAnimationStart', 'webkitAnimationStart'].map(function(event) {
        document.addEventListener(event, function (ev) {
          var callbacks = animationCallbacks[ev.animationName] || [];
          var l = callbacks.length;
          var i = 0;
          if (!l) ev.stopImmediatePropagation();
          for (; i < l; i++) callbacks[i](ev.target);
        }, true);
      });

      // add stylesheet to document
      styleEl = document.createElement('style');
      document.head.appendChild(styleEl);
      styleSheet = styleEl.sheet;
      cssRules = styleSheet.cssRules;
    }

    // listify argument
    (isArray(cssSelectors) ? cssSelectors : [cssSelectors])

    // add css rules and cache callbacks
    .map(function(selector) {
      var animId = selectorToAnimationMap[selector];
      if (!(animId)) {
        // add new CSS listener

        // add keyframe rule
        cssRules[styleSheet.insertRule(
          '@keyframes ' + (selectorToAnimationMap[selector] = animId = 'sentinel-' + Math.random().toString(16).slice(2)) + '{from{transform:none}to{transform:none}}',
          cssRules.length
        )]._id =

        // add selector animation rule
        cssRules[styleSheet.insertRule(
          selector + '{animation-duration:0.0001s;animation-name:' + animId + (extraAnimations ? ',' + extraAnimations : '') + '}',
          cssRules.length
        )]._id = selector;
      }

      // add to callbacks
      (animationCallbacks[animId] = animationCallbacks[animId] || []).push(callback)
    });
  },
  off: function(cssSelectors, callback) {
    // listify argument
    (isArray(cssSelectors) ? cssSelectors : [cssSelectors])

    // iterate through rules
    .map(function(selector, animId) {
      // get animId
      if (!(animId = selectorToAnimationMap[selector])) return;

      // get callbacks
      var callbackList = animationCallbacks[animId]||[];
      var i = callbackList.length;

      // remove callback from list
      if (callback) {
        while (i--) {
          if (callbackList[i] === callback) callbackList.splice(i, 1);
        }
      }

      // exit if callbacks still exist
      if (callbackList.length) return;

      // clear cache and remove css rules
      i = cssRules.length;
      while (i--) {
        if (cssRules[i]._id == selector) styleSheet.deleteRule(i);
      }

      delete selectorToAnimationMap[selector];
      delete animationCallbacks[animId];
    });
  },
  reset: function(p) {
    selectorToAnimationMap = {};
    animationCallbacks = {};
    styleEl && styleEl.parentNode.removeChild(styleEl);
    styleEl=0;
  }
};

})(document, Array.isArray);

Mostly I retained the semantics exactly, but I made a few changes:

All up, I’m at 589 bytes.

After I decided to stop I then remembered that you’re using mozAnimationStart and webkitAnimationStart, but without @-moz-keyframes and @-webkit-keyframes they’re useless. If you care about compatibility, add the prefixed keyframes, if you care about size, remove the prefixed event names.

amorey commented 6 years ago

Nice! 589 sounds pretty good. I'll take a closer look at it.

timwis commented 6 years ago

Impressive optimization!

amorey commented 6 years ago

@chris-morgan Thanks for your suggestions! I incorporated your code into the latest version of SentinelJS (v0.0.3) and got the size down to 602 bytes (minified + gzipped, without --no-name): https://github.com/muicss/sentineljs/blob/master/src/sentinel.js