Efficiently replacing strings within an HTML block in Javascript - javascript

I am using Javascript(with Mootools) to dynamically build a large page using HTML "template" elements, copying the same template many times to populate the page. Within each template I use string keywords that need to be replaced to create the unique IDs. I'm having serious performance issues however in that it takes multiple seconds to perform all these replacements, especially in IE. The code looks like this:
var fieldTemplate = $$('.fieldTemplate')[0];
var fieldTr = fieldTemplate.clone(true, true);
fieldTr.removeClass('fieldTemplate');
replaceIdsHelper(fieldTr, ':FIELD_NODE_ID:', fieldNodeId);
parentTable.grab(fieldTr);
replaceIdsHelper() is the problem method according to IE9's profiler. I've tried two implementations of this method:
// Retrieve the entire HTML body of the element, replace the string and set the HTML back.
var html = rootElem.get('html').replace(new RegExp(replaceStr, 'g'), id);
rootElem.set('html', html);
and
// Load the child elements and replace just their IDs selectively
rootElem.getElements('*').each(function(elem) {
var elemId = elem.get('id');
if (elemId != null) elemId = elemId.replace(replaceStr, id);
elem.set('id', elemId)
});
However, both of these approaches are extremely slow given how many times this method gets called(about 200...). Everything else runs fine, it's only replacing these IDs which seems to be a major performance bottleneck. Does anyone know if there's a way to do this efficiently, or a reason it might be running so slow? The elements start hidden and aren't grabbed by the DOM until after they're created so there's no redrawing happening.
By the way, the reason I'm building the page this way is to keep the code clean, since we need to be able to create new elements dynamically after loading as well. Doing this from the server side would make things much more complicated.

I'm not 100% sure, but it sounds to me that the problem is with the indexing of the dom tree.
First of all, do you must use ids or can you manage with classes? since you say that the replacement of the id is the main issue.
Also, why do you clone part of the dom tree instead of just inserting a new html?
You can use the substitute method of String (when using MooTools), like so:
var template = '<div id="{ID}" class="{CLASSES}">{CONTENT}</div>';
template.substitute({ID: "id1", CLASSES: "c1 c2", CONTENT: "this is the content" });
you can read more about it here http://mootools.net/docs/core/Types/String#String:substitute
Then, just take that string and put it as html inside a container, let's say:
$("container_id").set("html", template);
I think that it might improve the efficiency since it does not clone and then index it again, but I can't be sure. give it a go and see what happens.

there are some things you can do to optimise it - and what #nizan tomer said is very good, the pseudo templating is a good pattern.
First of all.
var fieldTemplate = $$('.fieldTemplate')[0];
var fieldTr = fieldTemplate.clone(true, true);
you should do this as:
var templateHTML = somenode.getElement(".fieldTemplate").get("html"); // no need to clone it.
the template itself should/can be like suggested, eg:
<td id="{id}">{something}</td>
only read it once, no need to clone it for every item - instead, use the new Element constructor and just set the innerHTML - notice it lacks the <tr> </tr>.
if you have an object with data, eg:
var rows = [{
id: "row1",
something: "hello"
}, {
id: "row2",
something: "there"
}];
Array.each(function(obj, index) {
var newel = new Element("tr", {
html: templateHTML.substitute(obj)
});
// defer the inject so it's non-blocking of the UI thread:
newel.inject.delay(10, newel, parentTable);
// if you need to know when done, use a counter + index
// in a function and fire a ready.
});
alternatively, use document fragments:
Element.implement({
docFragment: function(){
return document.createDocumentFragment();
}
});
(function() {
var fragment = Element.docFragment();
Array.each(function(obj) {
fragment.appendChild(new Element("tr", {
html: templateHTML.substitute(obj)
}));
});
// inject all in one go, single dom access
parentTable.appendChild(fragment);
})();
I did a jsperf test on both of these methods:
http://jsperf.com/inject-vs-fragment-in-mootools
surprising win by chrome by a HUGE margin vs firefox and ie9. also surprising, in firefox individual injects are faster than fragments. perhaps the bottleneck is that it's TRs in a table, which has always been dodgy.
For templating: you can also look at using something like mustache or underscore.js templates.

Related

How do I extract array data from the object <div class="gwt-Label">Some Data</div>?

How do I extract array data from the object <div class="gwt-Label">Some Data</div>?
Since #BrockAdams provided an excellent answer and solution for the general problem of data extraction from a DIV object, described by class name only (no id) and since the web page is made of 100+ DIV objects, described by the same class name, mainly "gwt-Label"
How do I extract the text from a (dynamic) div, by class name, using a userscript?
I am looking for a solution to limit the output to the console from 100+ lines to just few by modifying the code by #BrockAdams below
waitForKeyElements (".gwt-Label", printNodeText);
function printNodeText (jNode) {
console.log("gwt-Label value: ", jNode.text().trim());
}
Since the output I read in the console is 100+ lines long, but all I need is just a few selected lines by array index.
Do you know how to manipulate jNode to save output to an array first and have only the selected array elements to be reread and send to the console?
I would prefer pseudocode like this:
jNode.text().trim()[0]
jNode.text().trim()[5]
Run as a script in Greasemonkey or Tampermonkey.
And what's more, I need to loop the script over a numerical query string setting dynamic #match URL in the script.
Okay, if you have lots of class gwt-Label elements and, assuming that they are AJAX'd in in separate batches, you can put them into an array with code like this:
var valArry = [];
var ajxFinshTmr = 0;
waitForKeyElements (".gwt-Label", storeValue);
function storeValue (jNode) {
valArry.push (jNode.text ().trim () );
if (ajxFinshTmr) clearTimeout (ajxFinshTmr);
//-- Let all initial AJAX finish, so we know array is complete.
ajxFinshTmr = setTimeout (printFinalArray, 200);
}
function printFinalArray () {
console.log ("The final values are: ", valArry);
}
Note that there are almost certainly more robust/efficient/sensible alternatives, but we need to see more of the true page, and the true goal, to engineer those.
ETA: I see you've just linked to the site. Now describe in detail what you want to do. The possibilities can be quite messy.
it took me days to understand the problem
// ==UserScript==
// #name Important test1
// #namespace bbb
// #include http://srv1.yogh.io/#mine:height:0
// #version 1
// ==/UserScript==
console.log("sdfasg");
window.setTimeout(function() {
var HTMLCollection = document.getElementsByClassName("gwt-Label");
console.log(HTMLCollection);
var elements = HTMLCollection.length;
console.log(elements);
element = HTMLCollection[6];
console.log(element);
text = element.innerHTML;
console.log(text);
textclass= text.innerHTML;
// console.log(textclass);
console.log("15minutes");
}, 15000);
var HTMLCollection = document.getElementsByClassName();
console.log(HTMLCollection);
#BrockAdams was very helpful.
Since the above site loaded at random time duration so GM script one day worked fine, generating HTMLCollection in full but another time generated it empty, making innerHTML to generate undefined value.
Great success came with "353 more… ]" HTMLCollection link to Open In Variables View, generating exactly what I tried to accomplish via XPath, generating indexed (numered) list of all DIV objects in my HTMLCollection, to let me select DIV object of interest by known number.
I am still looking for alike solution provided by DOM Parsing and Serialization
[https://w3c.github.io/DOM-Parsing/#][1]
to work for me to append natural number, index to every DOM object of HTML document to let me use it in parallel with XPath generated by Firebug
example
html/body/div[3]/div[3]/div/div[6]/div[2]/div/div/div
followed by
html1/body2/div[3]5or-higher/div[3]/div/div[6]/div[2]/div/div/div
just serializing HTML DOM object, appending unique index to each object to let me call it via HTMLCollection[index] or better HTMLDOMCollection[index]
I am sure such approach has been known and adopted by HTML DOM serializer parser but I don't know how to access it.
thank you all

What is difference? Between creating html-element and simple adding text in html-document, when we're using JS? [duplicate]

In practice, what are the advantages of using createElement over innerHTML? I am asking because I'm convinced that using innerHTML is more efficient in terms of performance and code readability/maintainability but my teammates have settled on using createElement as the coding approach. I just wanna understand how createElement can be more efficient.
There are several advantages to using createElement instead of modifying innerHTML (as opposed to just throwing away what's already there and replacing it) besides safety, like Pekka already mentioned:
Preserves existing references to DOM elements when appending elements
When you append to (or otherwise modify) innerHTML, all the DOM nodes inside that element have to be re-parsed and recreated. If you saved any references to nodes, they will be essentially useless, because they aren't the ones that show up anymore.
Preserves event handlers attached to any DOM elements
This is really just a special case (although common) of the last one. Setting innerHTML will not automatically reattach event handlers to the new elements it creates, so you would have to keep track of them yourself and add them manually. Event delegation can eliminate this problem in some cases.
Could be simpler/faster in some cases
If you are doing lots of additions, you definitely don't want to keep resetting innerHTML because, although faster for simple changes, repeatedly re-parsing and creating elements would be slower. The way to get around that is to build up the HTML in a string and set innerHTML once when you are done. Depending on the situation, the string manipulation could be slower than just creating elements and appending them.
Additionally, the string manipulation code may be more complicated (especially if you want it to be safe).
Here's a function I use sometimes that make it more convenient to use createElement.
function isArray(a) {
return Object.prototype.toString.call(a) === "[object Array]";
}
function make(desc) {
if (!isArray(desc)) {
return make.call(this, Array.prototype.slice.call(arguments));
}
var name = desc[0];
var attributes = desc[1];
var el = document.createElement(name);
var start = 1;
if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) {
for (var attr in attributes) {
el[attr] = attributes[attr];
}
start = 2;
}
for (var i = start; i < desc.length; i++) {
if (isArray(desc[i])) {
el.appendChild(make(desc[i]));
}
else {
el.appendChild(document.createTextNode(desc[i]));
}
}
return el;
}
If you call it like this:
make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);
you get the equivalent of this HTML:
<p>Here is a link.</p>
User bobince puts a number of cons very, very well in his critique of jQuery.
... Plus, you can make a div by saying $(''+message+'') instead of having to muck around with document.createElement('div') and text nodes. Hooray! Only... hang on. You've not escaped that HTML, and have probably just created a cross-site-scripting security hole, only on the client side this time. And after you'd spent so long cleaning up your PHP to use htmlspecialchars on the server-side, too. What a shame. Ah well, no-one really cares about correctness or security, do they?
jQuery's not wholly to blame for this. After all, the innerHTML property has been about for years, and already proved more popular than DOM. But the library certainly does encourage that style of coding.
As for performance: InnerHTML is most definitely going to be slower, because it needs to be parsed and internally converted into DOM elements (maybe using the createElement method).
InnerHTML is faster in all browsers according to the quirksmode benchmark provided by #Pointy.
As for readability and ease of use, you will find me choosing innerHTML over createElement any day of the week in most projects. But as you can see, there are many points speaking for createElement.
While innerHTML may be faster, I don't agree that it is better in terms of readability or maintenance. It may be shorter to put everything in one string, but shorter code is not always necessarily more maintainable.
String concatenation just does not scale when dynamic DOM elements need to be created as the plus' and quote openings and closings becomes difficult to track. Consider these examples:
The resulting element is a div with two inner spans whose content is dynamic. One of the class names (warrior) inside the first span is also dynamic.
<div>
<span class="person warrior">John Doe</span>
<span class="time">30th May, 2010</span>
</div>
Assume the following variables are already defined:
var personClass = 'warrior';
var personName = 'John Doe';
var date = '30th May, 2010';
Using just innerHTML and mashing everything into a single string, we get:
someElement.innerHTML = "<div><span class='person " + personClass + "'>" + personName + "</span><span class='time'>" + date + "</span></div>";
The above mess can be cleaned up with using string replacements to avoid opening and closing strings every time. Even for simple text replacements, I prefer using replace instead of string concatenation.
This is a simple function that takes an object of keys and replacement values and replaces them in the string. It assumes the keys are prefixed with $ to denote they are a special value. It does not do any escaping or handle edge cases where $ appears in the replacement value etc.
function replaceAll(string, map) {
for(key in map) {
string = string.replace("$" + key, map[key]);
}
return string;
}
var string = '<div><span class="person $type">$name</span><span class="time">$date</span></div>';
var html = replaceAll(string, {
type: personClass,
name: personName,
date: date
});
someElement.innerHTML = html;
​This can be improved by separating the attributes, text, etc. while constructing the object to get more programmatic control over the element construction. For example, with MooTools we can pass object properties as a map. This is certainly more maintainable, and I would argue more readable as well. jQuery 1.4 uses a similar syntax to pass a map for initializing DOM objects.
var div = new Element('div');
var person = new Element('span', {
'class': 'person ' + personClass,
'text': personName
});
var when = new Element('span', {
'class': 'time',
'text': date
});
div.adopt([person, when]);
I wouldn't call the pure DOM approach below to be any more readable than the ones above, but it's certainly more maintainable because we don't have to keep track of opening/closing quotes and numerous plus signs.
var div = document.createElement('div');
var person = document.createElement('span');
person.className = 'person ' + personClass;
person.appendChild(document.createTextNode(personName));
var when = document.createElement('span');
​when.className = 'date​​​​​​';
when.appendChild(document.createTextNode(date));
​div.appendChild(person);
div.appendChild(when);
The most readable version would most likely result from using some sort of JavaScript templating.
<div id="personTemplate">
<span class="person <%= type %>"><%= name %></span>
<span class="time"><%= date %></span>
</div>
var div = $("#personTemplate").create({
name: personName,
type: personClass,
date: date
});
You should use createElement if you want to keep references in your code. InnerHTML can sometimes create a bug that is hard to spot.
HTML code:
<p id="parent">sample <span id='test'>text</span> about anything</p>
JS code:
var test = document.getElementById("test");
test.style.color = "red"; //1 - it works
document.getElementById("parent").innerHTML += "whatever";
test.style.color = "green"; //2 - oooops
1) you can change the color
2) you can't change color or whatever else anymore, because in the line above you added something to innerHTML and everything is re-created and you have access to something that doesn't exist anymore. In order to change it you have to again getElementById.
You need to remember that it also affects any events. You need to re-apply events.
InnerHTML is great, because it is faster and most time easier to read but you have to be careful and use it with caution. If you know what you are doing you will be OK.
Template literals (Template strings) is another option.
const container = document.getElementById("container");
const item_value = "some Value";
const item = `<div>${item_value}</div>`
container.innerHTML = item;

Preventing memory leaks when dynamically adding/removing HTML

I'm creating an app using PhoneGap and JQuery Mobile where I expect to be reading data from some backend service and adding/removing list items (potentially divs as well) based on what is received. Naturally, I want to prevent the app from slowing down too much with prolonged use.
Are there any general best practices when it comes to adding/removing HTML? I'm pretty new to this, so I don't really understand how the DOM works and whatnot.
Currently I am using JQuery's append to add lines of HTML to some container div, and using .html("") to remove an element. Is this acceptable, or is there a better way of doing this?
Edit: For your reference, I am using JQuery Mobile 1.1.0 and Phonegap 1.9.0. Don't know if that's helpful or not, but there you go.
I'm hoping to use this app across as many mobile devices as possible, but I guess iOS and Android are the biggest market right now.
To clarify, I haven't actually run into major issues (yet), I just wanted to avoid running into anything later on and having to go through a large amount of code to try to fix the problem. I figured it'd be useful for everyone if we could agree on a set of best practices, ie using innerHTML() over .append, remove vs innerHTML(""), et cetra :P
Some example code:
function changeList(sel){
var xmlfolder="/"
var name = sel[sel.selectedIndex].value+".xml";
var location= xmlfolder+name;
var list = document.getElementById("opList"); //opList is the container
list.innerHTML=""; //what i'm using to remove the previous occupant
//: is there a better way of doing this?
//reading stuff
$.ajax({
type:'GET',
cache: false,
url: location,
dataType:"xml",
success: function(data){
xmlInfo=data;
var i=0;
$(data).find("data").each(function(){
var string = "";
var sName=$(this).find("name").text();
string +=("<li>");
string +=("<a href=#details data-transition = slide >");
string+= sName;
string+="</a></li>";
$('#opList').append(string); //is there a better way of appending?
i++;
})
$('#opList').listview('refresh');
},
error:function(e){
alert("failed D:");
console.log(e);
}
});
}
I'm just going to throw a smattering of ideas at you and we'll see what sticks!
Make sure to concoct your HTML and add it to the DOM all at once rather than adding bits and pieces in a loop or something.
For Example (DO THIS):
var arr = ['Title 1', 'Title 2', 'Title 3'],
output = [];
for (var i = 0, len = arr.length; i < len; i++) {
output.push('<li>' + arr[i] + '</li>');
}
$('ul').append(output.join(''));
Notice how I only select the container once, this is key, even if you do have to reference a DOM element inside a loop, make sure to cache a reference to the element outside the loop, otherwise your're needlessly doing extra work every iteration.
And for example (DON'T DO THIS, except for the DOM element caching, that's good):
var arr = ['Title 1', 'Title 2', 'Title 3'],
$ul = $('ul');
for (var i = 0, len = arr.length; i < len; i++) {
$ul.append('<li>' + arr[i] + '</li>');
}
If you are going to add some complex HTML structure to the DOM, do the same thing as before, create a "temporary" container, do work on it, then append the whole thing to the DOM at once. For example:
var $tmpObj = $('<div id="my-div" />').append(
$('<span class="my-title" />').text('Title 1')
).append(
$('<div class="my-description" />').append(
$('<span class="my-copy" />').text('Blah Blah Blah')
)
).on('click', function () {
alert('Don\'t Touch Me!');
}).data('my-data-key', 'my-data-val');
$('#my-div').replaceWith($tmpObj);
I like to store data associated with an object using $.data() so that when an element is removed, so is it's data and therefore we aren't using-up memory with useless data.
Use JSPerf.com, it's an amazing tool when used properly. You can run tests to determine what method is faster for an operation. Here is an example one I setup a while ago: http://jsperf.com/jquery-each-vs-for-loops/2
Don't needlessly create global variables. When you do this it creates extra overhead when those variables are looked-up. This line: xmlInfo=data; should be var xmlInfo=data; unless xmlInfo is to be accessed outside of the AJAX callback function. Basically when you look-up a variable that is in the global scope while inside of a function (or nested functions); first the browser will look in the current scope, then the parent scope of the current one, all the way up to the global scope. So if you attempt to access a global variable inside of a function inside of a function, you're now doing about three times as much look-up as if you used a locally scoped variable. If you must use a globally scoped variable, cache it inside of your function and use the local version in your function to avoid the extra loop-up time.

Insert innerHTML with Prototypejs

Say I have a list like this:
<ul id='dom_a'>
<li>foo</li>
</ul>
I know how to insert elements in the ul tag with:
Element.insert('dom_a', {bottom:"<li>bar</li>"});
Since the string I receive contains the dom id, I need to insert the inner HTML instead of the whole element. I need a function to do this:
insert_content('dom_a', {bottom:"<ul id='dom_a'><li>bar</li></ul>"});
And obtain:
<ul id='dom_a'>
<li>foo</li>
<li>bar</li>
</ul>
How should I do this with Prototype ?
Here is the solution I have come up with, can anyone make this better ?
Zena.insert_inner = function(dom, position, content) {
dom = $(dom);
position = position.toLowerCase();
content = Object.toHTML(content);
var elem = new Element('div');
elem.innerHTML = content; // strip scripts ?
elem = elem.down();
var insertions = {};
$A(elem.childElements()).each(function(e) {
insertions[position] = e;
dom.insert(insertions);
});
}
I think you could parse the code block in your variable, then ask it for its innerHTML, and then use insert to stick that at the bottom of the actual node in the DOM.
That might look like this:
var rep_struct = "<ul id='dom_a'><li>bar</li></ul>";
var dummy_node = new Element('div'); // So we can easily access the structure
dummy_node.update(rep_struct);
$('dom_a').insert({bottom: dummy_node.childNodes[0].innerHTML});
I think you can slim down the code a bit by simply appending the innerHTML of the first child of temporary element:
Zena.insert_inner = function(dom, position, content) {
var d = document.createElement('div');
d.innerHTML = content;
var insertions = {};
insertions[position] = d.firstChild.innerHTML;
Element.insert(dom, insertions);
}
Not too much of an improvement though, example here.
I've been looking into the Prototype Documentation and I found this: update function.
By the way you described it, you could use the update function in order to find the current bottom content and then update it (just like innerHTML) by adding the desired code plus the previous stored code.
You could use regular expression to strip the outer element.
Element.Methods.insert_content = function(element, insertions) {
var regex = /^<(\w+)[^>]*>(.*)<\/\1>/;
for (key in insertions) {
insertions[key] = regex.exec(insertions[key])[2];
}
Element.insert(element, insertions);
};
Element.addMethods();
$('dom_a').insert_content({bottom:"<ul id='dom_a'><li>bar</li></ul>"});
If you are using PrototypeJS, you might also want to add script.aculo.us to your project. Builder in script.aculo.us provides a nice way to build complex DOM structures like so:
var myList = Builder.node("ul", {
id: "dom_a"
},[
Builder.node("li", "foo"),
Builder.node("li", "bar"),
]);
After this, you can insert this object which should be rendered as HTML anywhere in the DOM with any insert/update functions (of PrototypeJS) or even standard JavaScript appendChild.
$("my_div").insert({After: myList});
Note that in PrototypeJS insert comes in 4 different modes: After, Before, Top and Bottom. If you use insert without specifying a "mode" as above, the default will be Bottom. That is, the new DOM code will be appended below existing contents of the container element as innerHTML. Top will do the same thing but add it on top of the existing contents. Before and After are also cool ways to append to the DOM. If you use these, the content will be added in the DOM structure before and after the container element, not inside as innerHTML.
With Builder however, there is one thing to keep in mind, .. okay two things really:
i. You cannot enter raw HTML in the object as content... This will fail:
Builder.node("ul", "<li>foo</li>");
ii. When you specify node attributes, keep in mind that you must use className to signify HTML attribute class (and possibly also htmlFor for for attribute... although for attribute seems to be deprecated in HTML5(?), but who does not want to use it for labels)
Builder.node("ul", {
id: "dom_a",
className: "classy_list"
});
I know you are scratching your head because of point i. > What, no raw HTML, dang!
Not to worry. If you still need to add content which might contain HTML inside a Builder created DOM, just do it in the second stage using the insert({Before/After/Top/Bottom: string}). But why'd you want to do it in the first place? It would be really good practice if you wrote an once for all function that generates all kinds of DOM elements rather than stitching in all sorts of strings. The former approach would be neat and elegant. This is something like the inline style versus class type of question. Good design should after all separate content from meta content, or formatting markup / markdown.
One last thing to keep handy in your toolbox is Protype's DOM traversal in case you want to dynamically insert and delete content like a HTML Houdini. Check out the Element next, up, down, previous methods. Besides the $$ is also kinda fun to use, particularly if you know CSS3 selectors.

jQuery Tips and Tricks

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
Syntax
Shorthand for the ready-event by roosteronacid
Line breaks and chainability by roosteronacid
Nesting filters by Nathan Long
Cache a collection and execute commands on the same line by roosteronacid
Contains selector by roosteronacid
Defining properties at element creation by roosteronacid
Access jQuery functions as you would an array by roosteronacid
The noConflict function - Freeing up the $ variable by Oli
Isolate the $ variable in noConflict mode by nickf
No-conflict mode by roosteronacid
Data Storage
The data function - bind data to elements by TenebrousX
HTML5 data attributes support, on steroids! by roosteronacid
The jQuery metadata plug-in by Filip Dupanović
Optimization
Optimize performance of complex selectors by roosteronacid
The context parameter by lupefiasco
Save and reuse searches by Nathan Long
Creating an HTML Element and keeping a reference, Checking if an element exists, Writing your own selectors by Andreas Grech
Miscellaneous
Check the index of an element in a collection by redsquare
Live event handlers by TM
Replace anonymous functions with named functions by ken
Microsoft AJAX framework and jQuery bridge by Slace
jQuery tutorials by egyamado
Remove elements from a collection and preserve chainability by roosteronacid
Declare $this at the beginning of anonymous functions by Ben
FireBug lite, Hotbox plug-in, tell when an image has been loaded and Google CDN by Colour Blend
Judicious use of third-party jQuery scripts by harriyott
The each function by Jan Zich
Form Extensions plug-in by Chris S
Asynchronous each function by OneNerd
The jQuery template plug-in: implementing complex logic using render-functions by roosteronacid
Creating an HTML Element and keeping a reference
var newDiv = $("<div />");
newDiv.attr("id", "myNewDiv").appendTo("body");
/* Now whenever I want to append the new div I created,
I can just reference it from the "newDiv" variable */
Checking if an element exists
if ($("#someDiv").length)
{
// It exists...
}
Writing your own selectors
$.extend($.expr[":"], {
over100pixels: function (e)
{
return $(e).height() > 100;
}
});
$(".box:over100pixels").click(function ()
{
alert("The element you clicked is over 100 pixels height");
});
jQuery's data() method is useful and not well known. It allows you to bind data to DOM elements without modifying the DOM.
Nesting Filters
You can nest filters (as nickf showed here).
.filter(":not(:has(.selected))")
I'm really not a fan of the $(document).ready(fn) shortcut. Sure it cuts down on the code but it also cuts way down on the readability of the code. When you see $(document).ready(...), you know what you're looking at. $(...) is used in far too many other ways to immediately make sense.
If you have multiple frameworks you can use jQuery.noConflict(); as you say, but you can also assign a different variable for it like this:
var $j = jQuery.noConflict();
$j("#myDiv").hide();
Very useful if you have several frameworks that can be boiled down to $x(...)-style calls.
Ooooh, let's not forget jQuery metadata! The data() function is great, but it has to be populated via jQuery calls.
Instead of breaking W3C compliance with custom element attributes such as:
<input
name="email"
validation="required"
validate="email"
minLength="7"
maxLength="30"/>
Use metadata instead:
<input
name="email"
class="validation {validate: email, minLength: 2, maxLength: 50}" />
<script>
jQuery('*[class=validation]').each(function () {
var metadata = $(this).metadata();
// etc.
});
</script>
Live Event Handlers
Set an event handler for any element that matches a selector, even if it gets added to the DOM after the initial page load:
$('button.someClass').live('click', someFunction);
This allows you to load content via ajax, or add them via javascript and have the event handlers get set up properly for those elements automatically.
Likewise, to stop the live event handling:
$('button.someClass').die('click', someFunction);
These live event handlers have a few limitations compared to regular events, but they work great for the majority of cases.
For more info see the jQuery Documentation.
UPDATE: live() and die() are deprecated in jQuery 1.7. See http://api.jquery.com/on/ and http://api.jquery.com/off/ for similar replacement functionality.
UPDATE2: live() has been long deprecated, even before jQuery 1.7. For versions jQuery 1.4.2+ before 1.7 use delegate() and undelegate(). The live() example ($('button.someClass').live('click', someFunction);) can be rewritten using delegate() like that: $(document).delegate('button.someClass', 'click', someFunction);.
Replace anonymous functions with named functions. This really supercedes the jQuery context, but it comes into play more it seems like when using jQuery, due to its reliance on callback functions. The problems I have with inline anonymous functions, are that they are harder to debug (much easier to look at a callstack with distinctly-named functions, instead 6 levels of "anonymous"), and also the fact that multiple anonymous functions within the same jQuery-chain can become unwieldy to read and/or maintain. Additionally, anonymous functions are typically not re-used; on the other hand, declaring named functions encourages me to write code that is more likely to be re-used.
An illustration; instead of:
$('div').toggle(
function(){
// do something
},
function(){
// do something else
}
);
I prefer:
function onState(){
// do something
}
function offState(){
// do something else
}
$('div').toggle( offState, onState );
Defining properties at element creation
In jQuery 1.4 you can use an object literal to define properties when you create an element:
var e = $("<a />", { href: "#", class: "a-class another-class", title: "..." });
... You can even add styles:
$("<a />", {
...
css: {
color: "#FF0000",
display: "block"
}
});
Here's a link to the documentation.
instead of using a different alias for the jQuery object (when using noConflict), I always write my jQuery code by wrapping it all in a closure. This can be done in the document.ready function:
var $ = someOtherFunction(); // from a different library
jQuery(function($) {
if ($ instanceOf jQuery) {
alert("$ is the jQuery object!");
}
});
alternatively you can do it like this:
(function($) {
$('...').etc() // whatever jQuery code you want
})(jQuery);
I find this to be the most portable. I've been working on a site which uses both Prototype AND jQuery simultaneously and these techniques have avoided all conflicts.
Check the Index
jQuery has .index but it is a pain to use, as you need the list of elements, and pass in the element you want the index of:
var index = e.g $('#ul>li').index( liDomObject );
The following is much easier:
If you want to know the index of an element within a set (e.g. list items) within a unordered list:
$("ul > li").click(function () {
var index = $(this).prevAll().length;
});
Shorthand for the ready-event
The explicit and verbose way:
$(document).ready(function ()
{
// ...
});
The shorthand:
$(function ()
{
// ...
});
On the core jQuery function, specify the context parameter in addition to the selector parameter. Specifying the context parameter allows jQuery to start from a deeper branch in the DOM, rather than from the DOM root. Given a large enough DOM, specifying the context parameter should translate to performance gains.
Example: Finds all inputs of type radio within the first form in the document.
$("input:radio", document.forms[0]);
Reference: http://docs.jquery.com/Core/jQuery#expressioncontext
Not really jQuery only but I made a nice little bridge for jQuery and MS AJAX:
Sys.UI.Control.prototype.j = function Sys$UI$Control$j(){
return $('#' + this.get_id());
}
It's really nice if you're doing lots of ASP.NET AJAX, since jQuery is supported by MS now having a nice bridge means it's really easy to do jQuery operations:
$get('#myControl').j().hide();
So the above example isn't great, but if you're writing ASP.NET AJAX server controls, makes it easy to have jQuery inside your client-side control implementation.
Optimize performance of complex selectors
Query a subset of the DOM when using complex selectors drastically improves performance:
var subset = $("");
$("input[value^='']", subset);
Speaking of Tips and Tricks and as well some tutorials. I found these series of tutorials (“jQuery for Absolute Beginners” Video Series) by Jeffery Way are VERY HELPFUL.
It targets those developers who are new to jQuery. He shows how to create many cool stuff with jQuery, like animation, Creating and Removing Elements and more...
I learned a lot from it. He shows how it's easy to use jQuery.
Now I love it and i can read and understand any jQuery script even if it's complex.
Here is one example I like "Resizing Text"
1- jQuery...
<script language="javascript" type="text/javascript">
$(function() {
$('a').click(function() {
var originalSize = $('p').css('font-size'); // get the font size
var number = parseFloat(originalSize, 10); // that method will chop off any integer from the specified variable "originalSize"
var unitOfMeasure = originalSize.slice(-2);// store the unit of measure, Pixle or Inch
$('p').css('font-size', number / 1.2 + unitOfMeasure);
if(this.id == 'larger'){$('p').css('font-size', number * 1.2 + unitOfMeasure);}// figure out which element is triggered
});
});
</script>
2- CSS Styling...
<style type="text/css" >
body{ margin-left:300px;text-align:center; width:700px; background-color:#666666;}
.box {width:500px; text-align:justify; padding:5px; font-family:verdana; font-size:11px; color:#0033FF; background-color:#FFFFCC;}
</style>
2- HTML...
<div class="box">
Larger |
Smaller
<p>
In today’s video tutorial, I’ll show you how to resize text every time an associated anchor tag is clicked. We’ll be examining the “slice”, “parseFloat”, and “CSS” Javascript/jQuery methods.
</p>
</div>
Highly recommend these tutorials...
http://blog.themeforest.net/screencasts/jquery-for-absolute-beginners-video-series/
Asynchronous each() function
If you have really complex documents where running the jquery each() function locks up the browser during the iteration, and/or Internet Explorer pops up the 'do you want to continue running this script' message, this solution will save the day.
jQuery.forEach = function (in_array, in_pause_ms, in_callback)
{
if (!in_array.length) return; // make sure array was sent
var i = 0; // starting index
bgEach(); // call the function
function bgEach()
{
if (in_callback.call(in_array[i], i, in_array[i]) !== false)
{
i++; // move to next item
if (i < in_array.length) setTimeout(bgEach, in_pause_ms);
}
}
return in_array; // returns array
};
jQuery.fn.forEach = function (in_callback, in_optional_pause_ms)
{
if (!in_optional_pause_ms) in_optional_pause_ms = 10; // default
return jQuery.forEach(this, in_optional_pause_ms, in_callback); // run it
};
The first way you can use it is just like each():
$('your_selector').forEach( function() {} );
An optional 2nd parameter lets you specify the speed/delay in between iterations which may be useful for animations (the following example will wait 1 second in between iterations):
$('your_selector').forEach( function() {}, 1000 );
Remember that since this works asynchronously, you can't rely on the iterations to be complete before the next line of code, for example:
$('your_selector').forEach( function() {}, 500 );
// next lines of code will run before above code is complete
I wrote this for an internal project, and while I am sure it can be improved, it worked for what we needed, so hope some of you find it useful. Thanks -
Syntactic shorthand-sugar-thing--Cache an object collection and execute commands on one line:
Instead of:
var jQueryCollection = $("");
jQueryCollection.command().command();
I do:
var jQueryCollection = $("").command().command();
A somewhat "real" use case could be something along these lines:
var cache = $("#container div.usehovereffect").mouseover(function ()
{
cache.removeClass("hover").filter(this).addClass("hover");
});
I like declare a $this variable at the beginning of anonymous functions, so I know I can reference a jQueried this.
Like so:
$('a').each(function() {
var $this = $(this);
// Other code
});
Save jQuery Objects in Variables for Reuse
Saving a jQuery object to a variable lets you reuse it without having to search back through the DOM to find it.
(As #Louis suggested, I now use $ to indicate that a variable holds a jQuery object.)
// Bad: searching the DOM multiple times for the same elements
$('div.foo').each...
$('div.foo').each...
// Better: saving that search for re-use
var $foos = $('div.foo');
$foos.each...
$foos.each...
As a more complex example, say you've got a list of foods in a store, and you want to show only the ones that match a user's criteria. You have a form with checkboxes, each one containing a criteria. The checkboxes have names like organic and lowfat, and the products have corresponding classes - .organic, etc.
var $allFoods, $matchingFoods;
$allFoods = $('div.food');
Now you can keep working with that jQuery object. Every time a checkbox is clicked (to check or uncheck), start from the master list of foods and filter down based on the checked boxes:
// Whenever a checkbox in the form is clicked (to check or uncheck)...
$someForm.find('input:checkbox').click(function(){
// Start out assuming all foods should be showing
// (in case a checkbox was just unchecked)
var $matchingFoods = $allFoods;
// Go through all the checked boxes and keep only the foods with
// a matching class
this.closest('form').find("input:checked").each(function() {
$matchingFoods = $matchingFoods.filter("." + $(this).attr("name"));
});
// Hide any foods that don't match the criteria
$allFoods.not($matchingFoods).hide();
});
It seems that most of the interesting and important tips have been already mentioned, so this one is just a little addition.
The little tip is the jQuery.each(object, callback) function. Everybody is probably using the jQuery.each(callback) function to iterate over the jQuery object itself because it is natural. The jQuery.each(object, callback) utility function iterates over objects and arrays. For a long time, I somehow did not see what it could be for apart from a different syntax (I don’t mind writing all fashioned loops), and I’m a bit ashamed that I realized its main strength only recently.
The thing is that since the body of the loop in jQuery.each(object, callback) is a function, you get a new scope every time in the loop which is especially convenient when you create closures in the loop.
In other words, a typical common mistake is to do something like:
var functions = [];
var someArray = [1, 2, 3];
for (var i = 0; i < someArray.length; i++) {
functions.push(function() { alert(someArray[i]) });
}
Now, when you invoke the functions in the functions array, you will get three times alert with the content undefined which is most likely not what you wanted. The problem is that there is just one variable i, and all three closures refer to it. When the loop finishes, the final value of i is 3, and someArrary[3] is undefined. You could work around it by calling another function which would create the closure for you. Or you use the jQuery utility which it will basically do it for you:
var functions = [];
var someArray = [1, 2, 3];
$.each(someArray, function(item) {
functions.push(function() { alert(item) });
});
Now, when you invoke the functions you get three alerts with the content 1, 2 and 3 as expected.
In general, it is nothing you could not do yourself, but it’s nice to have.
Access jQuery functions as you would an array
Add/remove a class based on a boolean...
function changeState(b)
{
$("selector")[b ? "addClass" : "removeClass"]("name of the class");
}
Is the shorter version of...
function changeState(b)
{
if (b)
{
$("selector").addClass("name of the class");
}
else
{
$("selector").removeClass("name of the class");
}
}
Not that many use-cases for this. Never the less; I think it's neat :)
Update
Just in case you are not the comment-reading-type, ThiefMaster points out that the toggleClass accepts a boolean value, which determines if a class should be added or removed. So as far as my example code above goes, this would be the best approach...
$('selector').toggleClass('name_of_the_class', true/false);
Update:
Just include this script on the site and you’ll get a Firebug console that pops up for debugging in any browser. Not quite as full featured but it’s still pretty helpful! Remember to remove it when you are done.
<script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
Check out this link:
From CSS Tricks
Update:
I found something new; its the the JQuery Hotbox.
JQuery Hotbox
Google hosts several JavaScript libraries on Google Code. Loading it from there saves bandwidth and it loads quick cos it has already been cached.
<script src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
// Load jQuery
google.load("jquery", "1.2.6");
google.setOnLoadCallback(function() {
// Your code goes here.
});
</script>
Or
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
You can also use this to tell when an image is fully loaded.
$('#myImage').attr('src', 'image.jpg').load(function() {
alert('Image Loaded');
});
The "console.info" of firebug, which you can use to dump messages and variables to the screen without having to use alert boxes. "console.time" allows you to easily set up a timer to wrap a bunch of code and see how long it takes.
console.time('create list');
for (i = 0; i < 1000; i++) {
var myList = $('.myList');
myList.append('This is list item ' + i);
}
console.timeEnd('create list');
Use filtering methods over pseudo selectors when possible so jQuery can use querySelectorAll (which is much faster than sizzle). Consider this selector:
$('.class:first')
The same selection can be made using:
$('.class').eq(0)
Which is must faster because the initial selection of '.class' is QSA compatible
Remove elements from a collection and preserve chainability
Consider the following:
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
$("li").filter(function()
{
var text = $(this).text();
// return true: keep current element in the collection
if (text === "One" || text === "Two") return true;
// return false: remove current element from the collection
return false;
}).each(function ()
{
// this will alert: "One" and "Two"
alert($(this).text());
});
The filter() function removes elements from the jQuery object. In this case: All li-elements not containing the text "One" or "Two" will be removed.
Changing the type of an input element
I ran into this issue when I was trying to change the type of an input element already attached to the DOM. You have to clone the existing element, insert it before the old element, and then delete the old element. Otherwise it doesn't work:
var oldButton = jQuery("#Submit");
var newButton = oldButton.clone();
newButton.attr("type", "button");
newButton.attr("id", "newSubmit");
newButton.insertBefore(oldButton);
oldButton.remove();
newButton.attr("id", "Submit");
Judicious use of third-party jQuery scripts, such as form field validation or url parsing. It's worth seeing what's about so you'll know when you next encounter a JavaScript requirement.
Line-breaks and chainability
When chaining multiple calls on collections...
$("a").hide().addClass().fadeIn().hide();
You can increase readability with linebreaks. Like this:
$("a")
.hide()
.addClass()
.fadeIn()
.hide();
Use .stop(true,true) when triggering an animation prevents it from repeating the animation. This is especially helpful for rollover animations.
$("#someElement").hover(function(){
$("div.desc", this).stop(true,true).fadeIn();
},function(){
$("div.desc", this).fadeOut();
});
Using self-executing anonymous functions in a method call such as .append() to iterate through something. I.E.:
$("<ul>").append((function ()
{
var data = ["0", "1", "2", "3", "4", "5", "6"],
output = $("<div>"),
x = -1,
y = data.length;
while (++x < y) output.append("<li>" + info[x] + "</li>");
return output.children();
}()));
I use this to iterate through things that would be large and uncomfortable to break out of my chaining to build.
HTML5 data attributes support, on steroids!
The data function has been mentioned before. With it, you are able to associate data with DOM elements.
Recently the jQuery team has added support for HTML5 custom data-* attributes. And as if that wasn't enough; they've force-fed the data function with steroids, which means that you are able to store complex objects in the form of JSON, directly in your markup.
The HTML:
<p data-xyz = '{"str": "hi there", "int": 2, "obj": { "arr": [1, 2, 3] } }' />
The JavaScript:
var data = $("p").data("xyz");
data.str // "hi there"
typeof data.str // "string"
data.int + 2 // 4
typeof data.int // "number"
data.obj.arr.join(" + ") + " = 6" // "1 + 2 + 3 = 6"
typeof data.obj.arr // "object" ... Gobbles! Errrghh!

Categories