Skip to content

Instantly share code, notes, and snippets.

@abernier
Last active November 14, 2019 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abernier/6461914 to your computer and use it in GitHub Desktop.
Save abernier/6461914 to your computer and use it in GitHub Desktop.
JavaScript tips

ES6

modules

// geom.js

// function Geom() { ... }
// funtion area() { ... }
// funtion volume() { ... }

export default Geom;
export {area, volume};
import Geom, {area, volume} from './geom.js';

Destructuring

const { 🔧, 🔩 } = 🧰

Nested

arguments Destructuring

function myFunc({name, age}) {
  console.log(name, age)
}

myFunc({name: 'John', age: 25, married: false});

with default values:

function myFunc({name="John", age=18}) {
  console.log(name, age)
}

myFunc({});

optional object (to avoid a Uncaught TypeError: Cannot destructure property name of 'undefined' or 'null'. when calling myFunc() without object as parameter)

function myFunc({name="John", age=18} = {}) {
  console.log(name, age)
}

myFunc(); // No error

Renaming:

function myFunc({fullname: name="John", age=18} = {}) {
  console.log(name, age)
}

myFunc({fullname: 'John Doe'}); // No error

see: https://simonsmith.io/destructuring-objects-as-function-parameters-in-es6

Monkeypatch

Patching a function.

var randomOld = Math.random;
Math.random = function () {
  console.log("🐵");

  return randomOld.apply(Math, arguments);
};
> Math.random()
🐵
0.3729338688495576

Node binary

based on https://github.com/nizaroni/hackmd-to-html/pull/1/files#diff-9ea21889e9fbc7d82e7dabb0fb280808

#!/usr/bin/env node

const fs = require('fs');
const readline = require('readline');

 const hackmdToHtml = require('../dist/main.js')

 function help() {
  return `Convert hackMD syntax to HTML:
   npx hackmd-to-html MDFILE
   npx hackmd-to-html <MDFILE
   cat MDFILE | npx hackmd-to-html
   echo "# Hello" | npx hackmd-to-html
   npx hackmd-to-html
   npx hackmd-to-html -
`;
}

let html;
try {
  let markdown = '';

  function thenMarkdown() {
    html = hackmdToHtml(markdown);

    console.log(html);
    process.exit(0);
  }

  //
  // stdin > markdown variable
  //
  process.stdin.setEncoding('utf8');
  process.stdin.on('readable', () => {
    let chunk;
    while ((chunk = process.stdin.read()) !== null) {
      markdown += chunk;
    }
  });
  process.stdin.on('end', () => {
    thenMarkdown()
  });

  const argv2 = process.argv[2] && process.argv[2].trim();
  const mdfilepath = argv2 || '-'; // default to '-'

  if (argv2 === '--help' || argv2 === '-h') {
    console.log(help());
    process.exit(0);
  } else if (mdfilepath === '-') {
    var rl = readline.createInterface({
      input: process.stdin,
      prompt: ''
    });

    rl.prompt();
  } else {
    markdown = fs.readFileSync(mdfilepath, "utf8");
    thenMarkdown();
  }
} catch(e) {
  console.log(help());

  console.log(e);
  process.exit(1);
}

Tweenmax

draw/anim pattern

function Level(el) {
  this.$el = $(el);
  this.el = this.$el[0];

  this.$panels = this.$el.find('.panel');
  
  this.openness = .5;
}
Level.prototype.draw = function (openness) {
  this.$panels.css('transform', 'translate3d(0,'+(100*this.openness+'%')+',0)');
};
Level.prototype.anim = function (from, to, dur) {
  var o = {val: from};

  return TweenMax.fromTo(o, dur, {val: from}, {
    val: to,
    onUpdate: function (o) {
      this.draw(o.val);
    }.bind(this),
    onUpdateParams: [o]
  });
};

1s pattern

  1. Take 1s long as animation length
  2. Set your different tweens inside that 1s tl
  3. Scale the total duration
function Foo(el) {
  this.$el = $(el);
  this.$svgimage = this.$el.find('svg');
}
Foo.prototype.anim = function (duration) {
  var dur = 1; // 1s as length reference

  this.tl && this.tl.kill();
  this.tl = new TimelineMax();

  var tw1 = TweenMax.fromTo(this.$svgimage, dur/2, blahblah);
  var tw2 = TweenMax.fromTo(this.$svgimage, dur/2, blahblah);

  this.tl.add(tw1, 0);
  this.tl.add(tw2, .2);

  (typeof duration !== 'undefined') && this.tl.timeScale(this.tl.totalDuration()/(duration+.000001));
  return this.tl;
}

transitionend

const TRANSITIONEND = (function () {
  var prefixes = {
    'transition': 'transitionend',
    'OTransition': 'otransitionend',
    'MozTransition': 'transitionend',
    'WebkitTransition': 'webkitTransitionEnd'
  };

  for (var k in prefixes) {
    if (prefixes.hasOwnProperty(k) && document.documentElement.style[k] !== undefined) {
      return prefixes[k];
    }
  }

  return;
}).call(this);

var foo = document.getElementById('foo');

function dosomethingAfterOpacityTransition(e) {
  console.log('dosomethingAfterOpacityTransition');
  if (!(e.target === foo && e.propertyName === 'opacity')) {
    return;
  }

  // Opacity transition has ended here! Do something...
  console.log('transition has ended')
  
  foo.removeEventListener(TRANSITIONEND, dosomethingAfterOpacityTransition);
  e.stopPropagation();
}

foo.addEventListener(TRANSITIONEND, dosomethingAfterOpacityTransition);

transform property

var TRANSFORM = ["transform", "webkitTransform", "MozTransform", "msTransform"].filter(function (prop) {
  return prop in document.documentElement.style;
})[0];

Load with progress

function progress(url, tick, cb) {
  tick || (tick = function () {});
  cb || (cb = function () {});

  var xhr = new XMLHttpRequest();
  xhr.onprogress = function (e) {
    if (e.lengthComputable) {
      tick(e.loaded / e.total);
    }
  };
  xhr.onload = function () {
    cb(null);
  };
  xhr.onerror = function (er) {
    cb(er);
  };
  xhr.open("GET", url, true);
  xhr.send();
}

and smoke it with:

progress('http://abernier.name/images/iscool-pelican.png', function (x) {console.log(x);}, function (er) {
  if (er) console.error(er); return;
  
  console.log('done');
});

responsive images

<img width="{w}" height="{h}'>
<noscript>
  <img src="{file1}" data-srcset="{file1} 320w,{file2} 320w 2x, {file3} 768w,{file4} 768w 2x, {file5} 1024w,{file6} 1024w 2x, {file6} 3500w">
</noscript>
  • {w} et {h} devant être la largeur et hauteur de {file1}
  • file devant être des retailles de la même image inscrite dans un carré de :
  • 160px
  • 320px
  • 1088px
  • 1536px
  • 1792px
  • 2048px

$.fn.imgsloaded

;(function () {
  //
  // one <img>
  //
  function imgloaded(src) {
    var dfd = $.Deferred();
    
    var img = document.createElement('img');
    img.onload = dfd.resolve;
    img.src = src;
    
    return dfd.promise();
  }
  
  //
  // all inner <img>s
  //
  $.fn.imgsloaded = function (options) {
    var $el = this;
  
    var dfds = [];
    $el.each(function (i, el) {
      var $imgs = $(el).find('img');
      $imgs.each(function (i, el) {
        dfds.push(imgloaded($(el).attr('src')));
      });
    });
  
    return $.when.apply($, dfds);
  };

}).call(this);

// Usage:
$('.foo, .bar').then(function () {
  alert('all innert .foo and .bar images are now fully loaded!');
});

imgloaded

function imgloaded(src, cb) {
  var img = document.createElement('img');
  img.onload = function () {
    cb(null);
  }
  img.src = src;
}

// Usage:
imgloaded('http://placehold.it/1600x600', function () {
  alert('img is fully loaded');
})

promise things

function promiseSomething() {
  var dfd = $.Deferred();
  
  // dfd.resolve(arg1, arg2)
  // dfd.reject()
  
  return dfd.promise();
}

// Usage
promiseSomething()
  .done(function (arg1, arg2) {alert('hurray!');})
  .fail(function () {alert('too bad!');})
  ;

Environment independent require (browser or server)

var $ = (function () {return this; /* ie: global or window */}()).jQuery || require('jquery');

or more simply:

var $ = this.jQuery || require('jquery');

Get image "natural" ratio

function getImageRatio(src, cb) {
  var img = new Image();
  img.onload = function () {
      var w = img.width;
      var h = img.height;
      
      var ratio = w/h;
      
      cb(null, ratio);
  }
  img.src = src;
}

// Usage
getImageRatio('http://lorempixel.com/150/240/', function (ratio) {
  alert(ratio);
});

$.fn.redman

(function($) {
  
  var defaults = {
    klass: 'red',
    autopaint: true
  }

  //
  // Constructor
  //

  function Redman(el, options) {
    options = $.extend({}, defaults, options); // Merge with defaults
    this.options = options;
    
    var $el = $(el);
    var el = $el[0];

    this.$el = $el;
    this.el = el;

    // auto paint
    if (options.autopaint !== false) {
      this.paint();
    }
  }
  Redman.prototype.paint = function () {
    this.$el.addClass(this.options.klass);
  };
  Redman.prototype.unpaint = function () {
    this.$el.removeClass(this.options.klass);
  };

  //
  // Plugin
  //

  $.fn.redman = function (options) {
    var $el = this;

    return $el.each(function () {
      var redman = new Redman($el, options);

      $el.data('redman', redman); // save as expando
    });
  };

  $.fn.redman.defaults = defaults;

}(this.jQuery));           

//
// Usage
//

var $foo = $('#foo');
$foo.redman({
  klass: 'rouge'
});
$foo.data('redman').unpaint();

backup/restore CSS styles

// backup
$(el).data('original-cssText', el.style.cssText);

// restore
el.style.cssText = $(el).data('original-cssText');

Method/function invocation pattern

var o = {
    value: 3
};

* Method invocation

When invoking a function that is a property of an object

o.incr = function () {
    this.value += 1;  // 'this' will be bound to 'o'
};
o.incr();  // Method invocation pattern

* Function invocation

When invoking a function that is not a property of an object

o.double = function () {
    var that = this;  // save the context! (Workaround)

    function helper() {
        //this.value *= 2;
        that.value *= 2;
    };

    helper();  // Function invocation pattern: 'this' will be bound to the global object
};
o.double();

Callback pattern

function asyncMult(x, y, cb) {
  var ret = x * y;

  if (_.isNumber(ret)) {
    cb(null, ret); // Ok
  } else {
    cb(new Error('fail !')); // Problem
  }
}

asyncMult(2,3, function (er, result) {
  if (er) return er;

  // otherwise, we've got the result: 6
})

Also see: http://callbackhell.com

check a variable exists

Short version:

(typeof x !== 'undefined' && x !== null)

Long version, why all this?

A variable exists as soon as it is declared:

var toto; // ok, toto exists!
;         // but here, tata does not (not declared)

now, let's try to define our exists function...

function exists(x) {
  //
}

we can't do this:

if (x) {
  return true;
} else {
  return false;
}

since it will throw an Error: ReferenceError: x is not defined

so we need typeof:

if (typeof x !== 'undefined') {
  return true;
} else {
  return false;
}

but since typeof null equals "object" we need to make sure x does not exist while being set to null, thus:

if (typeof x !== 'undefined' && x !== null) {
  return true;
} else {
  return false;
}

so

function exists(x) {
  if (typeof x !== 'undefined' && x !== null) {
    return true;
  } else {
    return false;
  }
}

commonJS module

function foo(x) {
  return 'foo' + x;
}

this.foo = foo; // this equals the global object: window or exports
if (typeof module !== "undefined" && module !== null) { // check module variable exists
  module.exports = this.foo;
}

then simply:

var foo = require('./foo');
console.log(foo(2)); // foo2

or

<script src="path/to/foo.js"></script>
<script>
alert(foo(2));
</script>

environment independent require

var $ = this.jQuery || require('jquery');

clone an array

var arr1 = [1, 2, 3];

var arr2 = arr.slice();

assert.deepEqual(arr1, arr2, "same values");
assert.notStrictEqual(arr1, arr2, "but not same object in memory")

clone an object

see: http://stackoverflow.com/a/12826757/133327

var o1 = {a: 'a', b: {c: 'c'}};

var o2 = JSON.parse(JSON.stringify(o1));

assert.deepEqual(o1, o2, "same values");
assert.notStrictEqual(o1, o2, "but not same object in memory")

Caveats: functions will be stringified, as well as Dates — use _.clone here

"class"ical inheritance

Principle: in javascript, every function - which is also an Object - has a prototype property. Why? just because functions can also be used as constructors with the new operator. If you do, then the new created object will be linked to that prototype object and will inherit from it.

function toto() {/*hey*/}

toto.prototype exists is now:

{
  constructor: function toto() {/*hey*/} // allow us to keep a reference to constructor
}

Simple:

function MyError(message) {
  this.name = "MyError";
  this.message = message;
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;

More complete:

// Inheritance utility (see: http://coffeescript.org/#try:class%20A%0Aclass%20B%20extends%20A)
function inherit(child, parent) {
  // shallow copy own properties
  for (var key in parent) {
    if (parent.hasOwnProperty(key)) {
      child[key] = parent[key];
    }
  }

  function ctor() {this.constructor = child;}
  ctor.prototype = parent.prototype;
  child.prototype = new ctor();

  child.uber = parent.prototype; // keep a reference to parent's prototype into child.uber

  return child;
};

which can be used like:

function Animal(name) {
  this.name = name;
}
Animal.prototype.move = function (meters) {
  alert(this.name + ' moved ' + meters);
}

function Snake() {
  Animal.prototype.constructor.apply(this, arguments); // borrow parent's constructor
}
inherit(Snake, Animal); // Snake.uber = Animal.prototype
Snake.prototype.move = function () {
  alert('Slithering...');
  Animal.prototype.move.call(this, 5); // borrow parent's move method
}

See: inherit.js

walk

// Define a walk_the_DOM function that visits every
// node of the tree in HTML source order, starting
// from some given node. It invokes a function,
// passing it each node in turn. walk_the_DOM calls
// itself to process each of the child nodes.
var walk_the_DOM = function walk(node, func) {
  func(node);
  node = node.firstChild;
  while (node) {
    walk(node, func);
    node = node.nextSibling;
  }
};

relative CSS path

//
// relative CSS path
//
// Example:
// 
// <html>
//   <head>
//     <meta charset="utf-8">
//     <title></title>
//   </head>
//   ...
//
// var el = document.querySelector('title');
// var ref = document.querySelector('html')
// getPath(el, ref)
//
// Outputs: ":nth-child(1) > :nth-child(2)"
//
// If then you want to retreive an element from a path: $(<path>, ref);
//
var getPath = (function () {
  function nth(el) {
    return $(el).parent().children().index(el) + 1;
  }
  
  return function (el, ref, path) {
    path || (path = []);

    // for the 1st iteration, check el is contained within ref, otherwise return undefined
    if (path.length < 1 && !$.contains(ref, el)) {
      return;
    }

    var $el = $(el);
    var $parent = $el.parent();
    var parent = $parent[0];

    var i = nth(el);
    path.unshift(':nth-child(' + i + ')');

    if (parent === ref) {
      return path.join(' > '); // stop condition
    }

    return getPath(parent, ref, path);
  };
}());

waitUntil

//
// Wait until the test is true or a timeout occurs.
//
//   - If test succeeds before timeout, cb is executed with [null, <elapsed time>]
//   - If test does not succeed before timeout, cb is executed with [<error>]
//

function waitUntil(test, duration, cb, options) {
  options || (options = {delay: 1}); // every 1ms by default
  duration || (duration = 3000);

  var start = new Date().getTime();
  var finish = start + duration;
  var int;

  function looop() {
    var time = new Date().getTime();
    var timeout = (time >= finish);
    var condition = test();

    // No more time or condition fulfilled
    if (condition) {
      cb(null, time - start); // passed elapsed time
      clearInterval(int);
    }

    // THEN, no moretime but condition unfulfilled
    if (timeout && !condition) {
      var er = new Error('waitUntil has timeouted and the test has not passed.');
      cb(er);
      clearInterval(int);
    }
  }

  int = setInterval(looop, options.delay);
}

Assignement if not defined

options = options || {};

or

options || (options = {});

Existential

if (typeof god !== "undefined" && god !== null) {
  pray();
}

JSONP

A script tag in the document (no cross domain limitation for it) pointing to a remote resource, which returns a function executed back in the document

<script src="http://example.com/data.json" type="text/javascript" charset="utf-8"></script>
  > jsonCallback({"data": "foo"})
window.jsonCallback = function (result) { // Do stuff with the result }

or

jQuery.getJSON("http://example.com/data.json?callback=?", function (result) {
  // Do stuff with the result
});

Object.create

if (typeof Object.create !== "function") {
  Object.create = function(o) {
    function F() {};
    F.prototype = o;
    return new F();
  };
}

Proxy

var proxy = function(func, thisObject){
  return(function(){
    return func.apply(thisObject, arguments);
  });
};

Reserved words

abstract boolean break byte case catch char class const continue debugger default delete do double else enum export extends false final finally float for function goto if implements import in instanceof int interface long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var volatile void while with
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment