Skip to content

Instantly share code, notes, and snippets.

@thingsinjars
Forked from 140bytes/LICENSE.txt
Created December 12, 2011 09:39
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save thingsinjars/1466219 to your computer and use it in GitHub Desktop.
Save thingsinjars/1466219 to your computer and use it in GitHub Desktop.
Chainable DOM Manipulation

The two functions mentioned here, chainify (https://gist.github.com/gists/1466219) and propertize (https://gist.github.com/1466253) work fine on their own but best when used together.

Chainify

When provided with a constructor such as Element, Node, CSSStyleDeclaration, etc, the function will run through every property. If the property is a function, it will be wrapped in another function which will ensure that the return value is the original object.

This means that you can use the syntax

    document.createElement('a')
        .setAttribute('data-monkeys', '∞')
        .addEventListener('click', function(){ alert('Urk! Alert!')}, false)

Propertize

Similar to the chainify function, this takes a constructor as its argument. It adds the method .prop to everything which will set the properties provided either as an object or as a propertyName and propertyValue then return the original object to ensure the outcome is still chainable.

    myElement
        .prop({
            'href': 'http://140byt.es/',
            'title': 'tiny awsm'
        })
        .prop('innerHTML', '140byt.es');

When the two are combined, you have a completely chainable DOM manipulation tool.

Chainvas

The idea for the .prop function came from Lea Verou's Chainvas. This should be mostly compatible with the syntax (although possibly less robust). You can do the same chainify/propertize on any of the standard constructors - Element, Node, CSSStyleDeclaration, DOMTokenList, CanvasRenderingContext2D to make any API chainable.

function(
a, // The constructor of the group to make chainable
// e.g. Window.Element, Window.Node, Window.DOMTokenList etc...
b // Placeholder
c // Placeholder
) {
for(a in // Reusing this variable as we don't need it after the next line
// It's now used for the methods in the prototype of the constructor
b=a.prototype) // Shorthand to work with the prototype of the constructor
with({d:b[a]}) // Assign the method in here, creating a closure without needing another function wrapper
typeof d=='function' // if this is a function
? // Then
b[a]=function(){ // make every method a function which
return
(
c=d.apply(this,arguments)// does its job then assigns its return value
)===a._ // if the return value was undefined
? // Then
this // return 'this'
: // Else
c // return the intended return value
}
: // Else (this isn't a function)
0 // Do nothing
}
function(a,b,c){for(a in b=a.prototype)with({d:b[a]})typeof d=='function'?b[a]=function(){return(c=d.apply(this,arguments))===a._?this:c}:0}
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2011 Simon Madine <http://thingsinjars.com>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "chainify",
"description": "Extremely minimal chainable DOM manipulation based on Chainvas",
"keywords": [
"html",
"DOM",
"chaining"
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<title>Chain anything</title>
<div>Expected value: <b><a href="http://140byt.es/" title="tiny awsm" data-monkeys="∞">140byt.es</a></b></div>
<div>Actual value: <b id="ret"></b></div>
<script>
// This is paired with the propertize gist to cover chainability and setting properties
//This is a very, very minimal version of the functionality of Chainvas (http://lea.verou.me/chainvas/) to make anything chainable
var chainify = function(a,b,c){for(a in b=a.prototype)with({d:b[a]})typeof d=='function'?b[a]=function(){return(c=d.apply(this,arguments))===a._?this:c}:0}
// see https://gist.github.com/gists/1466253
var propertize = function(a,b,c){a.prototype.prop=function(){b=arguments;if(1 in b)this[b[0]]=b[1];else{b=b[0];for(c in b)this[c]=b[c]}return this}}
chainify(window.Element);
propertize(window.Element);
document.getElementById('ret').appendChild(
document.createElement('a')
.prop({
'href': 'http://140byt.es/',
'title': 'tiny awsm'
})
.prop('innerHTML', '140byt.es')
.setAttribute('data-monkeys', '∞')
.addEventListener('click', function(){ alert('Urk! Alert!')}, false)
);
</script>
@thingsinjars
Copy link
Author

I started messing around testing whether something is a function by using (typeof d)[0]=='f' before deciding it just felt wrong.

Besides, I don't think it works in IE7.

@nikola
Copy link

nikola commented Jan 1, 2012

But this works in IE7+

/^f/.test(typeof b)

Well, saves 1 byte.

@williammalo
Copy link

Since with() is deprecated and unreliable, I remade your function to not use with().

chainify = function(a,b){for(b in a)(function(c){a[b]=function(){c.apply(this,arguments);return this}})(a[b])}

chainify(window.Element.prototype)

Feel free to use it :)

@tjbenton
Copy link

tjbenton commented Mar 6, 2014

Note: These updates include calling the function on window.Element or window.Element.prototype

Seperate functions --------------------

Original prop - 174 bytes
var propertize=function(a,b,c){a.prototype.prop=function(){if(b=arguments,1 in b)this[b[0]]=b[1];else{b=b[0];for(c in b)this[c]=b[c]}return this}};propertize(window.Element);

Updated prop - 141 bytes
!function(a,b,c){a.prototype.prop=function(){b=arguments,1 in b?this[b[0]]=b[1]:b=b[0];for(c in b)this[c]=b[c];return this}}(window.Element);

Original chanify - 166 bytes
var chainify=function(a,b,c){for(a in b=a.prototype)with({d:b[a]})d.call?b[a]=function(){return(c=d.apply(this,arguments))===[]._?this:c}:0};chainify(window.Element);

Updated chanify - 129 bytes
!function(a,b){for(b in a=a.prototype)(function(c){a[b]=function(){return c.apply(this,arguments),this}})(a[b])}(window.Element);

Combined functions --------------------

Original prop and chanify - 336 bytes
var propertize=function(a,b,c){a.prototype.prop=function(){if(b=arguments,1 in b)this[b[0]]=b[1];else{b=b[0];for(c in b)this[c]=b[c]}return this}},chainify=function(a,b,c){for(a in b=a.prototype)with({d:b[a]})d.call?b[a]=function(){return(c=d.apply(this,arguments))===[]._?this:c}:0};propertize(window.Element),chainify(window.Element);

Updated prop and chanify - 228 bytes
!function(a,b,c){a.prop=function(){b=arguments,1 in b?this[b[0]]=b[1]:b=b[0];for(c in b)this[c]=b[c];return this};for(b in a=a)(function(c){a[b]=function(){return c.apply(this,arguments),this}})(a[b])}(window.Element.prototype);

@tunnckoCore
Copy link

@tjbenton awesome! use codeblocks...

@thingsinjars, need update of the gist because it is 2015 now :)

Based on above
propertize - 123 bytes

function(a,b,c){a.prototype.prop=function(){b=arguments,1 in b?this[b[0]]=b[1]:b=b[0];for(c in b)this[c]=b[c];return this}}

chainify - 111 bytes

function(a,b){for(b in a=a.prototype)(function(c){a[b]=function(){return c.apply(this,arguments),this}})(a[b])}

Combined propertize + chainify - 200 bytes - test pass!

function(a,b,c){a.prop=function(){b=arguments,1 in b?this[b[0]]=b[1]:b=b[0];for(c in b)this[c]=b[c];return this};for(b in a=a)(function(c){a[b]=function(){return c.apply(this,arguments),this}})(a[b])}

test pass

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