I'm doing quite a bit of DOM manipulation in my app, adding new nodes, and I've found that the children() function can get out of sync. I've got a tbody element with two rows, I use the children() function on this to do some manipulation with these rows. I then add two more rows to the tbody, when I use the children function again to do more manipulation I only get back the original two rows, not these plus the two rows I've just added. I'm doing a new call to children every time, not relying on any variable to auto-update. Is there any way to clear jQuery's cache - I've noticed problems like this a few times with selectors and got around it by selecting further up the DOM tree then navigating back down (i.e. don't select the tbody with a jQuery CSS selector, select the table then do table.tBodies[0].rows), but that won't work in this case.
Thanks,
Phil
The children() function doesn't get out of sync. You have to execute that again if you changed the DOM. On modifying you can have a callback that remakes your array of matched elements. This is not a cache mechanism, is like JavaScript works: linear. Ajax calls are sometimes cached but doesn't seem the case here.
Recommendation
Always use the latest version of jQuery: 1.3.2 now.
Update. So this is your code:
function removeGroup() {
var groupNodes = [ $('#row1')[0], $('#row2') ]; // is this correct?
var parent = groupNodes[0].parentNode;
if ( $(parent).children().length > 2) {
parent.removeChild(groupNodes[0]); // why not use dirrectly $('#row1')[0].remove();?
parent.removeChild(groupNodes[1]); // same here
} else {
alert("Can't delete last group");
}
}
This code has nothing to do with caching and most of it doenst even pass through jQuery.
Better explain what you want to do and will do it.
Related
Does jQuery cache selector lookups?
I am wondering whether this code
if ($("#ItemID").length) {
doSomethingWith($("#ItemID"));
}
will be significantly slower than this code
item = $("#ItemID");
if (item.length) {
doSomethingWith(item);
}
What about if you're extracting much more of the DOM e.g., $("div") rather than just $("#ItemID")? Same answer?
External references/explanation would be helpful, rather than just opinion.
According to the jQuery Learning Center, "jQuery doesn't cache elements for you. If you've made a selection that you might need to make again, you should save the selection in a variable rather than making the selection repeatedly."
This is particularly important when your selector is slow by its nature. For example, $("#foo") makes a call under the covers to document.getElementById() which should be very fast. However, a selector like $("a[rel$='thinger']") might be significantly slower on an older browser.
Remember that jQuery supports chaining, so you don't always have to introduce a variable to save the selection, you can just chain sequential calls together. For example, in this code I don't introduce a variable just to save the result of .find("img"), rather I chain three calls to prop and one to show.
clusterBlock.find("img").prop("src", clusterElt.imageUri)
.prop("alt", clusterElt.description)
.prop("id", clusterElt.imageId)
.show();
Finally, remember that a saved selection is not updated as the DOM changes. If I get all elements with class "foo" and save that in a variable, then some other piece of code adds and removes a couple of "foo" elements, then my saved selection will be missing the new elements and contain the references to the removed elements.
This
var item = $("#ItemID");
if (item.length) {
doSomethingWith(item);
}
will be faster but marginally. doSomethingWith will go to pointer where object is stored.
Turns out people do cache selectors btw
I wanna select some item by jQuery which has been added after loading page,so I wanna use live() function.I used it before for clicking like following code:
$("selector").live('click')
but now when I wanna use it in another function.
but It will not work with out argument,like it live()
for e.g followin code will alert test (work)
var pos_eq=Math.abs($('.myList').css("left").replace("px","")/$('.myList').children('li').eq(0).css('width').replace("px","")) + 1;
alert("test");
but this will not.
var pos_eq=Math.abs($('.myList').live().css("left").replace("px","")/$('.myList').live().children('li').eq(0).css('width').replace("px","")) + 1;
alert("test");
how can I solve it?
You want a function, not a variable. It looks like you are trying to keep pos_eq up to date after elements have been added to the page. Having a variable auto-update when the DOM changes in the way you are trying to do is not possible with JavaScript. What you can do is use a function instead of a variable. This way whenever the value is accessed you are getting the latest value because it is computed on demand:
function pos_eq() {
var list = $('.myList');
var left = parseInt(list.css("left"));
var width = parseInt(list.children('li').eq(0).css('width'));
return Math.abs(left / width) + 1;
}
I broke your code up into multiple statements to make it more readable. You would use this function the same as you used the variable, but instead add parens to the end to invoke the function:
alert(pos_eq);
alert(pos_eq());
To get a set of objects at the time you need them, just do $("selector"). That will do a query at that time and get the set of objects. There is no need to use .live() in order to query objects on the page. It does not matter whether the objects were part of the original page or were added dynamically later. When you do $("selector"), it will search the contents of the current page and get you the objects that are currently in the page that match the selector.
There is no way to do a live selector query and save it and have it automatically update in jQuery or any other library I know of. The way you solve that issue with a dynamic page is that you just do a new query when you need current results.
The description of live() is: Attach a handler to the event for all elements which match the current selector, now and in the future. It does not give you a live node list despite its name. jQuery does not have any method that returns a live node list(such as those returned by getElementsByTagName etc.) as far as I know.
I am authoring a simple jQuery plugin that turns an input tag into a time-formatted element (on blur it will change 245p into 2:45 pm).
Since I do not want to apply the time format events to the same element twice, I need a way to detect that the specific element in the list provided has not already had the format applied.
This is the relevant part of the code:
var methods = {
init : function(sel) {
var $this = $(sel);
return $this.each(function(){
var data = $(this).data('time_formatted');
if (data) {
return;
} else {
$(this).data('time_formatted', true);
I have heard that using $(sel).data() in a plugin is not a good idea; instead, use $.data(). I don't know why, that's just what I've heard; honestly, I don't know what the difference is.
So my question is, is this the way to accomplish checking if a specific element has had the time formatter applied to it in a plugin?
If you care to see the plugin in it's current development state, see http://jsfiddle.net/userdude/xhXCR/.
Thanks!
Jared
Where have you heard that using .data() is not good? jQuery's plugin autoring page says:
Often times in plugin development, you may need to maintain state or check if your plugin has already been initialized on a given element. Using jQuery's data method is a great way to keep track of variables on a per element basis. However, rather than keeping track of a bunch of separate data calls with different names, it's best to use a single object literal to house all of your variables, and access that object by a single data namespace.
So it should be perfectly fine.
To help demostrate what I am after, consider the following jsfiddle:
http://jsfiddle.net/5TNVK/4/
Except I want it to only inject the HTML in the corresponding selector, not every selector.
What I am attempting to do is have each selector match up with itself. On each ajax request, I get about 5 elements from the page I am loading and then animations and such with them. Currently I am first doing an ajax call then loop through an array of selectors assigning each html() the results of find(). This is very fast in all non-IE browsers, but takes about 1000 ms or more in IE8 when only finding 5 elements.
I figure there is an easier way that will ajax GET the page once, .find() every selector send to it and then insert the results to the same selector. Also it should only callback once.
I can't seem to grasp how to actually loop through each self. I would also want it so I just insert a url without the trailing selectors. The function would already know which selectors to use in .find() because it would be the same that were passed to it.
Ideally this is how it would work:
$('#foo, .bar, #test[href=moo]').my_load('newpage.html');
Where the current page's element with id=foo gets injected with newpage.html's element with id=foo, and the same for class=bar, etc.
Edit:
After some more benchmarking and such, it seems the .find() method is what is causing IE8 to be so slow in 1.4.4. finding 5 elements one by one in 3k html takes 1000ms in 1.4.4, in 1.3.2 it takes around 600ms.
Edit 2:
Keeping the way I had it before where I actually get the HTML source beforehand, I am using this function to parse the html source and get the selectors. This runs in about 200 ms in IE. So thats an improvement but I've been told to beware of innerHTML.
jQuery.fn.extend({
super_load: function(html,callback ) {
var self = this;
var htmls = $(html).find(self.selector);
for (var i =0; i < self.length; i++){
self[i].innerHTML = htmls[i].innerHTML;
}
if ( callback ) {
callback.call(this,html);
}
return this;
}
});
I don't think you need to write your own loader. Try this (demo):
$(document).ready(function() {
var hdr = $('<div />').load('http://fiddle.jshell.net #header', function(){
$('#name').html( hdr.find('#name').html() );
$('.aiButton[id=login]').html( hdr.find('#login').html() );
});
});
I have a page that selects all the elements in a form and serializes them like this:
var filter = 'form :not([name^=ww],[id$=IDF] *,.tools *)';
var serialized = $(filter).serialize();
This works, unless the form gets around 600+ elements. Then the user gets s javascript error saying that the script is running slow and may make their browsers unresponsive. It then gives them the option to stop running the script.
I have tried running the filters separately, I have tried using .not on the selectors, then serializing them, but I run into one of two problems. Either it runs faster without the error, but also does not filter the elements, or it does filter the elements and gives me the slow script error.
Any ideas?
With 600+ elements this is going to be dead slow. You need to offer Sizzle (jQuery's selector engine) some opportunities for optimisation.
First, consider the fact that jQuery can use the natively-supported querySelectorAll method (in modern browsers) if your selector complies with the CSS3 spec (or at least to the extent of what's currently supported in browsers).
With your case, that would mean passing only one simple selector to :not instead of 3 (1 simple, 2 complex).
form :not([name^=ww])
That would be quite fast... although you're not being kind to browsers that don't support querySelectorAll.
Look at your selector and think about how much Sizzle has to do with each element. First it needs to get ALL elements within the page (you're not pre-qualifying the :not selector with a tag/class/id). Then, on each element it does the following:
(assume that it exits if a result of a check is false)
Check that the parent has an ancestor with the nodeName.toLowerCase() of form.
Check that it does not have a name attribute starting with ww (basic indexOf operation).
Check that it does not have an ancestor with an id attribute ending in IDF. (expensive operation)
Check that it does not have an ancestor with a class attribute containing tools.
The last two operations are slow.
It may be best to manually construct a filter function, like so:
var jq = $([1]);
$('form :input').filter(function(){
// Re-order conditions so that
// most likely to fail is at the top!
jq[0] = this; // faster than constructing a new jQ obj
return (
!jq.closest('[id$=IDF]')[0]
// this can be improved. Maybe pre-qualify
// attribute selector with a tag name
&& !jq.closest('.tools')[0]
&& this.name.indexOf('ww') !== 0
);
});
Note: that function is untested. Hopefully you get the idea...
Could you maybe just serialize the whole form and do your filtering on the backend? Also, why-oh-why is the form growing to 600+ fields?
use the :input selector to only select applicable elements..