Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
bling dot js

bling.js

Because you want the $ of jQuery without the jQuery.


You may be interested in bling.js if you get tired of the [].slice.call( document.querySelectorAll('.foo'), function(){ … rodeo. It does this:

// forEach over the qSA result, directly.
document.querySelectorAll('input').forEach(el => /* ... */);

// on() rather than addEventListener()
document.body.on('dblclick', evt => /* ... */);

// classic $ + on()
$('p').on('click', el => /* ... */);

It doesn't do anything else. This is not a jQuery equivalent.

Notes:

  • on() works on elements, document, window, and results from querySelector & querySelectorAll.
  • $ is qSA so if you're grabbing a single element you'll have to [0] it.
  • Bling plays well with authoring ES6
  • Resig explored this stuff a while ago: github.com/jeresig/nodelist
  • Bling doesn't work on Android 2.3 or iOS 5.0. Works everywhere else including IE8 (assuming Function.bind)

Nerdy implementation notes:

  • The NodeList prototype usually inherits from Object, so we move it to Array.
  • I'm curious how ES6/7 would let a NodeList be iterable and inherit from EventTarget
  • Setting Node.prototype.on = EventTarget.prototype.addEventListener is awesome. It works in Chrome/FF but not yet in IE/Safari.
  • I haven't set up any off() or trigger() to map to dispatchEvent & removeEventListener. I'm OK with that.
  • I'm using semi-standard for style. I tried standard sans-semicolons, but can't get used to it.
/* bling.js */
window.$ = document.querySelectorAll.bind(document);
Node.prototype.on = window.on = function (name, fn) {
this.addEventListener(name, fn);
}
NodeList.prototype.__proto__ = Array.prototype;
NodeList.prototype.on = NodeList.prototype.addEventListener = function (name, fn) {
this.forEach(function (elem, i) {
elem.on(name, fn);
});
}
@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Apr 30, 2015

It works!

thx browser-repl

cat bling.js test.js  | uglifyjs | pbcopy
repl safari
repl firefox
repl ie
repl chrome

image

test.js

/*
    npm install -g browser-repl
    cat bling.js test.js  | uglifyjs | pbcopy   # copy bling & tests to clipboard
    repl  # see list of browsers to try it in
    repl ie
    # paste and zoooom!
*/

var input1 = document.createElement('input')
input1.id = 'one'
document.body.appendChild(input1)

var input2 = document.createElement('input')
input2.id = 'two'
document.body.appendChild(input2)

$('input').on('click', function (e) {
  console.log('PASS!', e.target.id, ' clicked! ')
})
window.on('click', function (e) {
  console.log('Pass! window received click too!')
})

// either click 'em or this is for testing…

function simulateClick (elem) {
  var evt = document.createEvent('MouseEvents')
  evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
  var canceled = !elem.dispatchEvent(evt)
}

simulateClick(input1)

setTimeout(function () { simulateClick(input2)  }, 500)
Owner

paulirish commented Apr 30, 2015

It works!

thx browser-repl

cat bling.js test.js  | uglifyjs | pbcopy
repl safari
repl firefox
repl ie
repl chrome

image

test.js

/*
    npm install -g browser-repl
    cat bling.js test.js  | uglifyjs | pbcopy   # copy bling & tests to clipboard
    repl  # see list of browsers to try it in
    repl ie
    # paste and zoooom!
*/

var input1 = document.createElement('input')
input1.id = 'one'
document.body.appendChild(input1)

var input2 = document.createElement('input')
input2.id = 'two'
document.body.appendChild(input2)

$('input').on('click', function (e) {
  console.log('PASS!', e.target.id, ' clicked! ')
})
window.on('click', function (e) {
  console.log('Pass! window received click too!')
})

// either click 'em or this is for testing…

function simulateClick (elem) {
  var evt = document.createEvent('MouseEvents')
  evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
  var canceled = !elem.dispatchEvent(evt)
}

simulateClick(input1)

setTimeout(function () { simulateClick(input2)  }, 500)
@andybak

This comment has been minimized.

Show comment
Hide comment
@andybak

andybak Jun 15, 2015

I had a few of these spare - you can have them: ;;;;;;;

andybak commented Jun 15, 2015

I had a few of these spare - you can have them: ;;;;;;;

@goyote

This comment has been minimized.

Show comment
Hide comment
@goyote

goyote Jun 15, 2015

Who says we don't want jQuery? (serious q)

goyote commented Jun 15, 2015

Who says we don't want jQuery? (serious q)

@avinoamr

This comment has been minimized.

Show comment
Hide comment
@avinoamr

avinoamr Jun 15, 2015

http://vanilla-js.com/

Because jQuery was born many years ago when browser implementations were very different, and much of the advanced functionality (like querySelectorAll) was not implemented. Now, there's very little reason for jQuery IMO, except for the syntax.

http://vanilla-js.com/

Because jQuery was born many years ago when browser implementations were very different, and much of the advanced functionality (like querySelectorAll) was not implemented. Now, there's very little reason for jQuery IMO, except for the syntax.

@benjamingr

This comment has been minimized.

Show comment
Hide comment
@benjamingr

benjamingr Jun 15, 2015

Why not return this on the ons to get the fluid interface back? Also .off

Why not return this on the ons to get the fluid interface back? Also .off

@jomo

This comment has been minimized.

Show comment
Hide comment
@joa

This comment has been minimized.

Show comment
Hide comment
@joa

joa Jun 15, 2015

Nice! Maybe you can even use EventTarget.prototype.on instead of Node.prototype.on = window.on?

joa commented Jun 15, 2015

Nice! Maybe you can even use EventTarget.prototype.on instead of Node.prototype.on = window.on?

@r-cyr

This comment has been minimized.

Show comment
Hide comment
@r-cyr

r-cyr Jun 15, 2015

I personally dislike the term "VanillaJS", I think of it as a disservice to the community because it confuses people into thinking that jQuery is not JavaScript. Perhaps we should start using the term "VanillaDOM" instead?

r-cyr commented Jun 15, 2015

I personally dislike the term "VanillaJS", I think of it as a disservice to the community because it confuses people into thinking that jQuery is not JavaScript. Perhaps we should start using the term "VanillaDOM" instead?

@sukima

This comment has been minimized.

Show comment
Hide comment
@sukima

sukima Jun 15, 2015

Add ES6 Promises -> jQuery.Deferred API:

window.$.Deferred = function() {
  var resolver, rejector;
  var promise = new Promise(function(resolve, reject) {
    resolver = resolve;
    rejector = reject;
  });
  var deferred = {
    resolve: resolver,
    reject: rejector,
    then: function(a, b) { promise = promise.then(a, b); return deferred; },
    pipe: function(a, b) { promise = promise.then(a, b); return deferred; },
    done: function(a) { promise = promise.then(a, b); return deferred; },
    fail: function(a) { promise = promise.catch(a); return deferred; },
    always: function(a) { promise = promise.finally(a); return deferred; },
    promise: function() { return deferred; }
  };
  return deferred;
};

sukima commented Jun 15, 2015

Add ES6 Promises -> jQuery.Deferred API:

window.$.Deferred = function() {
  var resolver, rejector;
  var promise = new Promise(function(resolve, reject) {
    resolver = resolve;
    rejector = reject;
  });
  var deferred = {
    resolve: resolver,
    reject: rejector,
    then: function(a, b) { promise = promise.then(a, b); return deferred; },
    pipe: function(a, b) { promise = promise.then(a, b); return deferred; },
    done: function(a) { promise = promise.then(a, b); return deferred; },
    fail: function(a) { promise = promise.catch(a); return deferred; },
    always: function(a) { promise = promise.finally(a); return deferred; },
    promise: function() { return deferred; }
  };
  return deferred;
};
@peterchon

This comment has been minimized.

Show comment
Hide comment
@peterchon

peterchon Jun 15, 2015

I thought it was never a good idea to extend a native object? I'm not questioning Paul's expertise, was just curious.

I thought it was never a good idea to extend a native object? I'm not questioning Paul's expertise, was just curious.

@abritinthebay

This comment has been minimized.

Show comment
Hide comment
@abritinthebay

abritinthebay Jun 15, 2015

It's not that it's a bad idea to extend a native object - it's that you should do so fully knowing what problems that can cause. In fact there is a (very good) argument to be made (ie - it's supported by Douglas Crockford and Brandon Eich) that libraries are the best place to do so.

Extending Object and Array is fraught with peril for usage reasons but the others? Not so much.

It's not that it's a bad idea to extend a native object - it's that you should do so fully knowing what problems that can cause. In fact there is a (very good) argument to be made (ie - it's supported by Douglas Crockford and Brandon Eich) that libraries are the best place to do so.

Extending Object and Array is fraught with peril for usage reasons but the others? Not so much.

@SerkanSipahi

This comment has been minimized.

Show comment
Hide comment
@SerkanSipahi

SerkanSipahi Jun 15, 2015

see what i did one year ago:
1.) Simple: EventHandling + Delegation http://codepen.io/SerkanSipahi/pen/pEmyA?editors=101
2.) find method: http://codepen.io/SerkanSipahi/pen/rLeDF?editors=101

:)

see what i did one year ago:
1.) Simple: EventHandling + Delegation http://codepen.io/SerkanSipahi/pen/pEmyA?editors=101
2.) find method: http://codepen.io/SerkanSipahi/pen/rLeDF?editors=101

:)

@sirlancelot

This comment has been minimized.

Show comment
Hide comment
@sirlancelot

sirlancelot Jun 15, 2015

Your Readme indicates IE8 support, but I don't think IE8 has __proto__ support?

Your Readme indicates IE8 support, but I don't think IE8 has __proto__ support?

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Jun 15, 2015

Nice! Maybe you can even use EventTarget.prototype.on instead of Node.prototype.on = window.on?

@joa, I wanted to but the EventTarget global isn't available everywhere. Probably a way to set it by walking a prototype chain instead, but I'm lazy.

Add ES6 Promises -> jQuery.Deferred API:

@sukima wow. that's hot. very nice. :)

Why not return this on the ons to get the fluid interface back? Also .off

@benjamingr could, but as there are no other APIs, no need. pretty simple life with a small feature set. :) off() is so uncommon, I'd be OK with using removeEventListener, but hey that's me.

Owner

paulirish commented Jun 15, 2015

Nice! Maybe you can even use EventTarget.prototype.on instead of Node.prototype.on = window.on?

@joa, I wanted to but the EventTarget global isn't available everywhere. Probably a way to set it by walking a prototype chain instead, but I'm lazy.

Add ES6 Promises -> jQuery.Deferred API:

@sukima wow. that's hot. very nice. :)

Why not return this on the ons to get the fluid interface back? Also .off

@benjamingr could, but as there are no other APIs, no need. pretty simple life with a small feature set. :) off() is so uncommon, I'd be OK with using removeEventListener, but hey that's me.

@sinan

This comment has been minimized.

Show comment
Hide comment
@sinan

sinan Jun 16, 2015

this seems like vanilla.js joke but it really shouldn't be more complex than this :) Start with this and extend it to whatever utility you need e.g. $.ajax = require('some-xhr-module')

sinan commented Jun 16, 2015

this seems like vanilla.js joke but it really shouldn't be more complex than this :) Start with this and extend it to whatever utility you need e.g. $.ajax = require('some-xhr-module')

@niko

This comment has been minimized.

Show comment
Hide comment
@niko

niko Jun 16, 2015

Nice! A tiny bit of AJAX sugar would be nice.

niko commented Jun 16, 2015

Nice! A tiny bit of AJAX sugar would be nice.

@arturkulig

This comment has been minimized.

Show comment
Hide comment
@arturkulig

arturkulig Jun 16, 2015

maybe fetch wrapper for ajax and fetch-shim as dependency? ; D

maybe fetch wrapper for ajax and fetch-shim as dependency? ; D

@gregbenner

This comment has been minimized.

Show comment
Hide comment
@gregbenner

gregbenner Jun 16, 2015

Would be cool to take all the above suggestions, and add animations obviously.. oh wait that would be jQuery 2.x 😀

Nice sugar micro-library as is!

Would be cool to take all the above suggestions, and add animations obviously.. oh wait that would be jQuery 2.x 😀

Nice sugar micro-library as is!

@Dudemullet

This comment has been minimized.

Show comment
Hide comment
@Dudemullet

Dudemullet Jun 16, 2015

In case someone is interested, here's how to add very simple even delegation a-la jQuery. IE9+

Node.prototype.on = window.on = function (name, delegate, fn) {
  if(arguments.length !== 3) {
    return this.addEventListener(name, arguments[1]);
  }

  return this.addEventListener(name, function (e) {
    if(e.target.matches(delegate)){
      return fn.apply(e.target, arguments);
    }
  })
}

In case someone is interested, here's how to add very simple even delegation a-la jQuery. IE9+

Node.prototype.on = window.on = function (name, delegate, fn) {
  if(arguments.length !== 3) {
    return this.addEventListener(name, arguments[1]);
  }

  return this.addEventListener(name, function (e) {
    if(e.target.matches(delegate)){
      return fn.apply(e.target, arguments);
    }
  })
}
@suarasaur

This comment has been minimized.

Show comment
Hide comment
@suarasaur

suarasaur Jun 16, 2015

@paulirish standard is neat, semi-standard is neat, but then there's also javascript whatever

@paulirish standard is neat, semi-standard is neat, but then there's also javascript whatever

@JSila

This comment has been minimized.

Show comment
Hide comment
@JSila

JSila Jun 16, 2015

I implemented this behaviour just recently but not quite as elegant as this.
One note: IE8 lacks support for Array.prototype.forEach.

JSila commented Jun 16, 2015

I implemented this behaviour just recently but not quite as elegant as this.
One note: IE8 lacks support for Array.prototype.forEach.

@bjankord

This comment has been minimized.

Show comment
Hide comment
@bjankord

bjankord Jun 17, 2015

Love this implementation, you should turn it into a full fledged repo, you've got the code/readme/tests 😄

Was getting an "Object doesn't support property or method 'forEach'" in IE10/IE9 so I updated this lineNodeList.prototype.__proto__ = Array.prototype to NodeList.prototype.forEach = Array.prototype.forEach;

Love this implementation, you should turn it into a full fledged repo, you've got the code/readme/tests 😄

Was getting an "Object doesn't support property or method 'forEach'" in IE10/IE9 so I updated this lineNodeList.prototype.__proto__ = Array.prototype to NodeList.prototype.forEach = Array.prototype.forEach;

@jasonsperske

This comment has been minimized.

Show comment
Hide comment
@jasonsperske

jasonsperske Jun 17, 2015

Combine this with promise.js and get "Promise Bling"?

Combine this with promise.js and get "Promise Bling"?

@pankajbisht

This comment has been minimized.

Show comment
Hide comment
@pankajbisht

pankajbisht Jun 18, 2015

Paul you are right. If I worked on nodes, lot of time, I have faced this kind of problem. This is the nice solution for [].slice.call(). But querySelector/querySelectorAll have some browser issue so for this have you any solution?

Paul you are right. If I worked on nodes, lot of time, I have faced this kind of problem. This is the nice solution for [].slice.call(). But querySelector/querySelectorAll have some browser issue so for this have you any solution?

@ebsen

This comment has been minimized.

Show comment
Hide comment
@ebsen

ebsen Jun 22, 2015

Line 9 is my favorite.

ebsen commented Jun 22, 2015

Line 9 is my favorite.

@blaja

This comment has been minimized.

Show comment
Hide comment
@blaja

blaja Jun 24, 2015

I want moar! 😃 Great stuff.

blaja commented Jun 24, 2015

I want moar! 😃 Great stuff.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Jun 26, 2015

can be reffered to by its short url: git.io/blingjs

ghost commented Jun 26, 2015

can be reffered to by its short url: git.io/blingjs

@spiralx

This comment has been minimized.

Show comment
Hide comment
@spiralx

spiralx Jul 7, 2015

Line 9 should be replaced with the following:

Object.setPrototypeOf(NodeList.prototype, Array.prototype)

which is part of ES6 and currently supported in IE 11, FF and Chrome - for older browsers, this is a quick polyfill:

if (!Object.setPrototypeOf) {
  Object.setPrototypeOf = function(obj, proto) {
    obj.__proto__ = proto;
    return obj; 
  }
}

Also +1 for Node.prototype.on and NodeList.prototype.on returning this, my versions are:

  Node.prototype.on = window.on = function(names, fn) {
    var self = this

    names.split(' ').forEach(function(name) {
      self.addEventListener(name, fn)
    })

    return this
  }

  NodeList.prototype.on = NodeList.prototype.addEventListener = function(names, fn) {
    this.forEach(function(elem) {
      elem.on(names, fn)
    })

    return this
  }

which also allow binding multiple events like jQuery e.g. node.on('change blur', function(ev) { ... } :)

spiralx commented Jul 7, 2015

Line 9 should be replaced with the following:

Object.setPrototypeOf(NodeList.prototype, Array.prototype)

which is part of ES6 and currently supported in IE 11, FF and Chrome - for older browsers, this is a quick polyfill:

if (!Object.setPrototypeOf) {
  Object.setPrototypeOf = function(obj, proto) {
    obj.__proto__ = proto;
    return obj; 
  }
}

Also +1 for Node.prototype.on and NodeList.prototype.on returning this, my versions are:

  Node.prototype.on = window.on = function(names, fn) {
    var self = this

    names.split(' ').forEach(function(name) {
      self.addEventListener(name, fn)
    })

    return this
  }

  NodeList.prototype.on = NodeList.prototype.addEventListener = function(names, fn) {
    this.forEach(function(elem) {
      elem.on(names, fn)
    })

    return this
  }

which also allow binding multiple events like jQuery e.g. node.on('change blur', function(ev) { ... } :)

@dciccale

This comment has been minimized.

Show comment
Hide comment

have you tried ki.js? :D https://github.com/dciccale/ki.js

@hjpbarcelos

This comment has been minimized.

Show comment
Hide comment
@hjpbarcelos

hjpbarcelos Aug 19, 2015

Doing this is somewhat a bad practice:

NodeList.prototype.__proto__ = Array.prototype;

NodeList.prototype.on = NodeList.prototype.addEventListener = function (name, fn) {
  this.forEach(function (elem, i) {
    elem.on(name, fn);
  });
}

Don't mess with the native prototypes!

Why don't you try it this way?

window.$ = document.querySelectorAll.bind(document);

Node.prototype.on = window.on = Node.prototype.addEventListener;

NodeList.prototype.on = NodeList.prototype.addEventListener = function (name, fn) {
     Array.prototype.slice.call(this).forEach(function(elem) {
         elem.addEventListener(name, fn, false);
     });
}

Doing this is somewhat a bad practice:

NodeList.prototype.__proto__ = Array.prototype;

NodeList.prototype.on = NodeList.prototype.addEventListener = function (name, fn) {
  this.forEach(function (elem, i) {
    elem.on(name, fn);
  });
}

Don't mess with the native prototypes!

Why don't you try it this way?

window.$ = document.querySelectorAll.bind(document);

Node.prototype.on = window.on = Node.prototype.addEventListener;

NodeList.prototype.on = NodeList.prototype.addEventListener = function (name, fn) {
     Array.prototype.slice.call(this).forEach(function(elem) {
         elem.addEventListener(name, fn, false);
     });
}
@MadLittleMods

This comment has been minimized.

Show comment
Hide comment
@MadLittleMods

MadLittleMods Aug 20, 2015

What about changing the prototype of HTMLCollection as well so they are easier to iterate over? You can find HTMLCollection's in properties like ParentNode.children

Not sure of the side effects, yet.

HTMLCollection.prototype.__proto__ = Array.prototype;

What about changing the prototype of HTMLCollection as well so they are easier to iterate over? You can find HTMLCollection's in properties like ParentNode.children

Not sure of the side effects, yet.

HTMLCollection.prototype.__proto__ = Array.prototype;
@stevermeister

This comment has been minimized.

Show comment
Hide comment
@stevermeister

stevermeister Oct 12, 2015

put code in github repo - https://github.com/stevermeister/bling.js/blob/master/bling.js
to make it open & conveninent for contribution

put code in github repo - https://github.com/stevermeister/bling.js/blob/master/bling.js
to make it open & conveninent for contribution

@gugadev

This comment has been minimized.

Show comment
Hide comment
@gugadev

gugadev Oct 10, 2016

Better $ to querySelector and $$ to querySelectorAll instead of $('.something')[0].on(...).

gugadev commented Oct 10, 2016

Better $ to querySelector and $$ to querySelectorAll instead of $('.something')[0].on(...).

@crislin2046

This comment has been minimized.

Show comment
Hide comment
@crislin2046

crislin2046 Nov 17, 2016

Inspired by this to make a version here https://github.com/dosaygo-coder-0/postjs/blob/master/postbling/src/postbling.js

What's new?

  • on applies to EventTargets and Arrays containing them, so you can do this:
$('*').slice(3).on('click', reveal );
$`div`.on( 'touchstart', dragOn ).on( 'touchend', dragOff );
  • NodeList and HTMLCollection both now delegate to Array
  • the on functions return this to allow chaining
  • and made it more ES5/6/7-y including Object.setPrototypeOf

These included ideas from @MadLittleMods and @spiralx and @joa

Inspired by this to make a version here https://github.com/dosaygo-coder-0/postjs/blob/master/postbling/src/postbling.js

What's new?

  • on applies to EventTargets and Arrays containing them, so you can do this:
$('*').slice(3).on('click', reveal );
$`div`.on( 'touchstart', dragOn ).on( 'touchend', dragOff );
  • NodeList and HTMLCollection both now delegate to Array
  • the on functions return this to allow chaining
  • and made it more ES5/6/7-y including Object.setPrototypeOf

These included ideas from @MadLittleMods and @spiralx and @joa

@skylarmb

This comment has been minimized.

Show comment
Hide comment
@skylarmb

skylarmb Mar 1, 2017

@bjankord awesome. thanks for the tip. Sadly some of us do have to support IE9... 🎉

skylarmb commented Mar 1, 2017

@bjankord awesome. thanks for the tip. Sadly some of us do have to support IE9... 🎉

@GaurangTandon

This comment has been minimized.

Show comment
Hide comment
@GaurangTandon

GaurangTandon Mar 17, 2017

This does NOT work for DOM elements inside iframes. Here's the related SO question with a related code sample - based on the same principles as above code - with a reproducible example - http://stackoverflow.com/questions/42825990/extending-prototype-of-dom-elements-inside-iframes
Any ideas anyone?

This does NOT work for DOM elements inside iframes. Here's the related SO question with a related code sample - based on the same principles as above code - with a reproducible example - http://stackoverflow.com/questions/42825990/extending-prototype-of-dom-elements-inside-iframes
Any ideas anyone?

@Ollie-w

This comment has been minimized.

Show comment
Hide comment
@Ollie-w

Ollie-w Jun 5, 2017

Is this safe to use in production?

Ollie-w commented Jun 5, 2017

Is this safe to use in production?

@paulirish

This comment has been minimized.

Show comment
Hide comment
Owner

paulirish commented Dec 2, 2017

@Ollie-w yeah totally.

@argyleink

This comment has been minimized.

Show comment
Hide comment
@argyleink

argyleink Apr 1, 2018

I think I have an interesting fork:

  • only mutates the nodes queried with the exported function
  • offers 'off'
  • offers nice attribute CRUD operations

https://github.com/argyleink/blingblingjs

argyleink commented Apr 1, 2018

I think I have an interesting fork:

  • only mutates the nodes queried with the exported function
  • offers 'off'
  • offers nice attribute CRUD operations

https://github.com/argyleink/blingblingjs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment