jquery data manipulate api - a source code dissecting journey
DESCRIPTION
Represent major data manipulate API in jQuery 1.6; such as .data(), removeData(), index(), globalEval() and so no. Also, HTML5 data-* attributes. I will walk you through with diving into jQuery source code and find out the killing techniques used in jQuery.TRANSCRIPT
jQuery Data Manipulate
http://thefronts.com/ http://deanyan.com/
http://www.linkedin.com/in/deanyan/
Dean Yan
A source code dissecting journey
HTML5 data-* Attributes
<li class="user" data-name="John Resig" data-city="Boston"data-lang="js" data-food="Bacon"> <b>John says:</b> <span>Hello, how are you?</span></li>
var user = document.getElementsByTagName("li")[0];var pos = 0, span = user.getElementsByTagName("span")[0];
var phrases = [ {name: "city", prefix: "I am from "}, {name: "food", prefix: "I like to eat "}, {name: "lang", prefix: "I like to program in "}];
user.addEventListener( "click", function(){ var phrase = phrases[ pos++ ]; // Use the .dataset property span.innerHTML = phrase.prefix + user.dataset[ phrase.name ];}, false);
REF> http://ejohn.org/blog/html-5-data-attributes/
HTML5 data-* Attributes
span.innerHTML = phrase.prefix + user.getAttribute("data-" + phrase.name )
The .dataset property behaves very similarly to the the .attributes property (but it only works as a map of key-value pairs)
HTML5 data-* AttributesWhile I firmly believe that useful data should be made visible to users,
there are circumstances where data-* attributes make sense. For instance, including data-lat and data-lng attributes in an element containing a street address would allow for easily adding markers to a Google Map on the page:
<span
data-lat="38.8951" data-lng="-77.0363">
1600 Pennsylvania Ave. Washington, DC
</span>
“
”
REF> http://www.viget.com/inspire/extending-paul-irishs-comprehensive-dom-ready-execution/
HTML5 data-* AttributesTaking a step back, we can use data-* attributes on the body
element to provide an indication of where we are within an application:
<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">
<body data-controller="users" data-action="show">
The above code will yield something like:
REF> http://www.viget.com/inspire/extending-paul-irishs-comprehensive-dom-ready-execution/
.data()/jQuery.data()
.data( key, value ).data( key, value ) .data( obj )
.data( key ).data( key ) .data()
.data()/jQuery.data()<div> The values stored were <span></span> and <span></span></div>
$("div").data("test", { first: 16, last: "pizza!" });$("span:first").text($("div").data("test").first);$("span:last").text($("div").data("test").last);
<div data-role="page" data-last-value="43" data-hidden="true" data-options='{"name":"John"}'></div>
$("div").data("role") === "page";$("div").data("lastValue") === 43;$("div").data("hidden") === true;$("div").data("options").name === "John";
.data()/jQuery.data()var myObj = {};$(myObj).data("city", "Springfield");
myObj.city = "Springfield"
var progressBar = {};
$(progressBar).bind('setData', function(e, key, value) { switch (key) { case "percent": $("#progress").width(value + "%"); $("#percentText").text(value + "%"); break;
case "color": $("#progress").css("color", value); break;
case "enabled": $('#progress').toggleClass("active", value); break; }});
$(progressBar).data("enabled", true).data("percent", 21).data("color","green");
// You also have easy access to the current values:console.log(progressBar.enabled); // true
.data()/jQuery.data()
getData – triggered before data is read from the object.
changeData – triggered whenever data is set or changed. It is used in the jQuery datalink plugin .
. removeData()/ jQuery.removeData()
.removeData( [ name ] )
<div>value1 before creation: <span></span></div><div>value1 after creation: <span></span></div><div>value1 after removal: <span></span></div>
$("span:eq(0)").text("" + $("div").data("test1"));
$("div").data("test1", "VALUE-1");$("div").data("test2", "VALUE-2");
$("span:eq(1)").text("" + $("div").data("test1"));$("div").removeData("test1");
$("span:eq(2)").text("" + $("div").data("test1"));$("span:eq(3)").text("" + $("div").data("test2"));
jQuery.hasData()
jQuery.hasData( element )
The primary advantage of jQuery.hasData(element) is that it does not create and associate a data object with the element if none currently exists. In contrast, jQuery.data(element) always returns a data object to the caller, creating one if no data object previously existed.
“
”
jQuery.hasData()
$(function(){ var $p = jQuery("p"), p = $p[0];
$p.append(jQuery.hasData(p)+" "); /* false */ jQuery.data(p, "testing", 123);
$p.append(jQuery.hasData(p)+" "); /* true*/ jQuery.removeData(p, "testing");
$p.append(jQuery.hasData(p)+" "); /* false */});
Utilities
jQuery.isEmptyObject()
jQuery.trim()
jQuery.isFunction()
jQuery.each()
jQuery.globalEval()
jQuery.merge()jQuery.proxy()
jQuery.extend()
Utilities
// Populate the class2type mapjQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { class2type["[object " + name + "]"] =name.toLowerCase();});
type: function(obj) { return obj == null ? String(obj) :class2type[toString.call(obj)] || "object";},
// See test/unit/core.js for details concerning isFunction.// Since version 1.3, DOM methods and functions like alert// aren't supported. They return false on IE (#2968).isFunction: function(obj) { return jQuery.type(obj) === "function";},
jQuery.isFunction()
Utilities
// Use native String.trim function wherever possibletrim: trim ?function(text) { return text == null ? "" : trim.call(text);} :
// Otherwise use our own trimming functionality
function(text) { return text == null ? "" :text.toString().replace(trimLeft,"")
.replace(trimRight, "");},
jQuery.trim()
Utilities
isEmptyObject: function(obj) { for (var name in obj) { return false; } return true;},
jQuery.isEmptyObject()
Utilities
isPlainObject: function(obj) { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well if (!obj || jQuery.type(obj) !== "object" ||obj.nodeType || jQuery.isWindow(obj)) { return false; }
// Not own constructor property must be Object if (obj.constructor && !hasOwn.call(obj,"constructor")
&&!hasOwn.call(obj.constructor.prototype,"isPrototypeOf")) { return false; }
// Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for (key in obj) {}
return key === undefined || hasOwn.call(obj,key);},
jQuery.isPlainObject()
Utilities
globalEval: function(data) { if (data && rnotwhite.test(data)) { // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox (window.execScript || function(data) { window["eval"].call(window, data); })(data); }},
jQuery.globalEval()
Utilities// Bind a function to a context, optionally partially applying any// arguments.proxy: function(fn, context) { if (typeof context === "string") { var tmp = fn[context]; context = fn; fn = tmp; }
// Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if (!jQuery.isFunction(fn)) { return undefined; }
// Simulated bind var args = slice.call(arguments, 2), proxy = function() { return fn.apply(context,args.concat(slice.call(arguments))); };
// Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || proxy.guid ||jQuery.guid++;
return proxy;},
jQuery.proxy()
UtilitiesjQuery.each()
// args is for internal usage onlyeach: function(object, callback, args) { var name, i = 0, length = object.length, isObj = length === undefined || jQuery.isFunction(object);
if (args) { if (isObj) { for (name in object) { if (callback.apply(object[name], args) === false) { break; } } } else { for (; i < length;) { if (callback.apply(object[i++], args) === false) { break; } } }
UtilitiesjQuery.each()
// A special, fast, case for the most common use of each } else { if (isObj) { for (name in object) { if (callback.call(object[name], name, object[name]) ===false) { break; } } } else { for (; i < length;) { if (callback.call(object[i], i, object[i++]) === false) { break; } } } }
return object;},
Miscellaneous
.index()
.map()
.get()
.toArray()
.slice()
Miscellaneous.index()
.index( selector )
selector A selector representing a jQuery collection in which to look for an element.
.index( element )
element The DOM element or first element within the jQuery object to look for.
Miscellaneous
<span>Click a div!</span><div>First div</div><div>Second div</div><div>Third div</div><script>
$("div").click(function () { // this is the dom element clicked
var index = $("div").index(this); $("span").text("That was div index #" + index);
});</script>
.index()
On click, returns the index (based zero) of that div in the page.
Miscellaneous
<ul> <li id="foo">foo</li> <li id="bar">bar</li> <li id="baz">baz</li></ul><div></div><script>$('div').html('Index: ' + $('#bar').index('li') );</script>
.index()
Returns the index for the element with ID bar in relation to all <li> elements.
Miscellaneous
toArray: function() { return slice.call(this, 0);}
Reversed - <span></span>
<div>One</div><div>Two</div><div>Three</div><script>function disp(divs) { var a = []; for (var i = 0; i < divs.length; i++) { a.push(divs[i].innerHTML); } $("span").text(a.join(" "));}
disp($("div").toArray().reverse());</script>
toArray()
Miscellaneous.map().map( callback(index, domElement) )callback(index, domElement)A function object that will be invoked for each element in the current set.
Miscellaneous
<input type="button" value="equalize div heights">
<div style="background:red; height: 40px; "></div><div style="background:green; height: 70px;"></div><div style="background:blue; height: 50px; "></div>
<script> $.fn.equalizeHeights = function(){
return this.height( Math.max.apply(this, $(this).map(function(i,e){ return $(e).height() }).get() ) ) }
$('input').click(function(){ $('div').equalizeHeights(); });</script>
.map()Equalize the heights of the divs.
Miscellaneous
map: function(callback) { return this.pushStack(jQuery.map(this, function(elem, i) { return callback.call(elem, i, elem); }));},
.map()
Miscellaneous// arg is for internal usage onlymap: function(elems, callback, arg) { var value, key, ret = [], i = 0, length = elems.length, // jquery objects are treated as arrays isArray = elems instanceof jQuery || length !== undefined &&typeof length === "number" && ((length > 0 && elems[0] && elems[length -1]) || length === 0 || jQuery.isArray(elems));
// Go through the array, translating each of the items to their if (isArray) { for (; i < length; i++) { value = callback(elems[i], i, arg);
if (value != null) { ret[ret.length] = value; } }
// Go through every key on the object, } else { for (key in elems) { value = callback(elems[key], key, arg);
if (value != null) { ret[ret.length] = value; } } }
// Flatten any nested arrays return ret.concat.apply([], ret);},
.map()
Miscellaneous// Take an array of elements and push it onto the stack// (returning the new matched element set)pushStack: function(elems, name, selector) { // Build a new jQuery matched element set var ret = this.constructor();
if (jQuery.isArray(elems)) { push.apply(ret, elems);
} else { jQuery.merge(ret, elems); }
// Add the old object onto the stack (as a reference) ret.prevObject = this;
ret.context = this.context;
if (name === "find") { ret.selector = this.selector + (this.selector ? " " : "") +selector; } else if (name) { ret.selector = this.selector + "." + name + "(" + selector +")"; }
// Return the newly-formed element set return ret;},
.map()
Miscellaneous
// Determine the position of an element within// the matched set of elementsindex: function(elem) { if (!elem || typeof elem === "string") { return jQuery.inArray(this[0], // If it receives a string, the selector is used // If it receives nothing, the siblings are used elem ? jQuery(elem) : this.parent().children()); } // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem.jquery ? elem[0] : elem, this);},
.index()
Miscellaneous
// Get the Nth element in the matched element set OR// Get the whole matched element set as a clean arrayget: function(num) { return num == null ?
// Return a 'clean' array this.toArray() :
// Return just the object (num < 0 ? this[this.length + num] : this[num]);},
.get()
General Attributes
.attr()
.removeAttr()
.prop()
.removeProp()
General Attributes
attr: function(name, value) { return jQuery.access(this, name, value, true, jQuery.attr);},
removeAttr: function(name) { return this.each(function() { jQuery.removeAttr(this, name); });},
.attr() .removeAttr()
General Attributes
prop: function(name, value) { return jQuery.access(this, name, value, true, jQuery.prop);},
removeProp: function(name) { return this.each(function() { // try/catch handles cases where IE balks (such as removing a property on window) try { this[name] = undefined; delete this[name]; } catch (e) {} });},
.prop() .removeProp()