I'm not sure if "cached" is a correct term for this one. Maybe I should use "instantiated" instead. However, say I want to "cache" several objects, to save some resources:
var $foo = $("#foo"),
$bar = $("#bar");
Now, if I want to hide them, can I use a one-liner instead of:
$foo.hide();
$bar.hide();
I reckon that this one is quite simple (read: "stupid"), but hey... I can't figure it out all by myself...
You can also use .add() to roll up a bunch of jQuery objects and selectors:
$foo.add( $bar ).add('.someclass').add( $other_objects_or_selectors ).hide();
you can use something like this:
$.each([$foo, $bar], function(i, v) {
v.hide();
});
You can have multiple selectors in jQuery separated with comma:
$("#foo, #bar").hide();
If you're doing this for many id's, maybe do something like:
items = ['foo', 'bar'];
cache = {};
$.each(items, function(i,key) {
cache[key] = $('#' + key);
cache[key].hide();
});
Then later on access the 'cache' like cache['foo'].
Define a class for all these elements and then hide by the class
$('#foo').addClass('toHide');
//Later
$('.toHide').filter(':visible').hide();
Related
How can I select nodes that begin with a "x-" tag name, here is an hierarchy DOM tree example:
<div>
<x-tab>
<div></div>
<div>
<x-map></x-map>
</div>
</x-tab>
</div>
<x-footer></x-footer>
jQuery does not allow me to query $('x-*'), is there any way that I could achieve this?
The below is just working fine. Though I am not sure about performance as I am using regex.
$('body *').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
Working fiddle
PS: In above sample, I am considering body tag as parent element.
UPDATE :
After checking Mohamed Meligy's post, It seems regex is faster than string manipulation in this condition. and It could become more faster (or same) if we use find. Something like this:
$('body').find('*').filter(function(){
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
UPDATE 2:
If you want to search in document then you can do the below which is fastest:
$(Array.prototype.slice.call(document.all)).filter(function () {
return /^x-/i.test(this.nodeName);
}).each(function(){
console.log(this.nodeName);
});
jsperf test
There is no native way to do this, it has worst performance, so, just do it yourself.
Example:
var results = $("div").find("*").filter(function(){
return /^x\-/i.test(this.nodeName);
});
Full example:
http://jsfiddle.net/6b8YY/3/
Notes: (Updated, see comments)
If you are wondering why I use this way for checking tag name, see:
JavaScript: case-insensitive search
and see comments as well.
Also, if you are wondering about the find method instead of adding to selector, since selectors are matched from right not from left, it may be better to separate the selector. I could also do this:
$("*", $("div")). Preferably though instead of just div add an ID or something to it so that parent match is quick.
In the comments you'll find a proof that it's not faster. This applies to very simple documents though I believe, where the cost of creating a jQuery object is higher than the cost of searching all DOM elements. In realistic page sizes though this will not be the case.
Update:
I also really like Teifi's answer. You can do it in one place and then reuse it everywhere. For example, let me mix my way with his:
// In some shared libraries location:
$.extend($.expr[':'], {
x : function(e) {
return /^x\-/i.test(this.nodeName);
}
});
// Then you can use it like:
$(function(){
// One way
var results = $("div").find(":x");
// But even nicer, you can mix with other selectors
// Say you want to get <a> tags directly inside x-* tags inside <section>
var anchors = $("section :x > a");
// Another example to show the power, say using a class name with it:
var highlightedResults = $(":x.highlight");
// Note I made the CSS class right most to be matched first for speed
});
It's the same performance hit, but more convenient API.
It might not be efficient, but consider it as a last option if you do not get any answer.
Try adding a custom attribute to these tags. What i mean is when you add a tag for eg. <x-tag>, add a custom attribute with it and assign it the same value as the tag, so the html looks like <x-tag CustAttr="x-tag">.
Now to get tags starting with x-, you can use the following jQuery code:
$("[CustAttr^=x-]")
and you will get all the tags that start with x-
custom jquery selector
jQuery(function($) {
$.extend($.expr[':'], {
X : function(e) {
return /^x-/i.test(e.tagName);
}
});
});
than, use $(":X") or $("*:X") to select your nodes.
Although this does not answer the question directly it could provide a solution, by "defining" the tags in the selector you can get all of that type?
$('x-tab, x-map, x-footer')
Workaround: if you want this thing more than once, it might be a lot more efficient to add a class based on the tag - which you only do once at the beginning, and then you filter for the tag the trivial way.
What I mean is,
function addTagMarks() {
// call when the document is ready, or when you have new tags
var prefix = "tag--"; // choose a prefix that avoids collision
var newbies = $("*").not("[class^='"+prefix+"']"); // skip what's done already
newbies.each(function() {
var tagName = $(this).prop("tagName").toLowerCase();
$(this).addClass(prefix + tagName);
});
}
After this, you can do a $("[class^='tag--x-']") or the same thing with querySelectorAll and it will be reasonably fast.
See if this works!
function getXNodes() {
var regex = /x-/, i = 0, totalnodes = [];
while (i !== document.all.length) {
if (regex.test(document.all[i].nodeName)) {
totalnodes.push(document.all[i]);
}
i++;
}
return totalnodes;
}
Demo Fiddle
var i=0;
for(i=0; i< document.all.length; i++){
if(document.all[i].nodeName.toLowerCase().indexOf('x-') !== -1){
$(document.all[i].nodeName.toLowerCase()).addClass('test');
}
}
Try this
var test = $('[x-]');
if(test)
alert('eureka!');
Basically jQuery selector works like CSS selector.
Read jQuery selector API here.
I needed to dynamically add properties to an js Object, which I achieved via eval():
$ ->
#Methods
window.add_address = (attributes, id=new Date().getTime())->
$container = $('ul#addresses_list')
$unit = $('<li>')
$.each attributes, (key,value)->
$input = $('<input type="hidden">')
$input.attr 'name', "contact[addresses_attributes][#{id}][#{key}]"
$input.val value
$unit.append $input
$container.append $unit
#Events
#Add address button
$('a#add_address').on 'click', (ev)->
attributes = new Object
$('#address_fields').find('input').each ->
eval("attributes.#{$(this).attr 'id'}='#{$(this).val()}'");
add_address attributes
This works perfect but I feel awkward with the eval(), is there anyway to do this "prettier"? I mean, I searched for alternatives like the Jquery .serializeArray() but it seems to work only with a queried form and I need to get the inputs from that #address_fields div.
Use object['key'] notation
attributes[$(this).attr('id')] = $(this).val();
It's also very efficient to create object using:
var attributes={};
EDIT: Along similar lines can write the jquery methods in same notation
attributes[$(this)['attr']('id')] = $(this)['val']();
Hi everyone,
Actually, i got "c.replace is not a function" while i was trying to delete some DOM elements and..i don't understand.
i'd like to delete some tags from the DOM and so, i did it :
var liste=document.getElementById("tabs").getElementsByTagName("li");
for(i=0;i<liste.length;i++)
{
if(liste[i].id==2)
{
$("#tabs").detach(liste[i]);
}
}
I tried .detach and .remove but it's the same. My version of jQuery is 1.7.1.min.js.
Thanks for help.
order of iteration on a NodeLIst
Doing forward iteration of a NodeList that is being modified when you remove an element can be an issue. Iterate in reverse when removing elements from the DOM.
misuse of detach()
Also, the arguments to .detach() do not perform a nested find, but rather act as a filter on the existing element(s) in the jQuery object, and should be passed a string. It seems that you actually want to detach the li, which would mean that you'd need to call .detach() on the li itself...
var liste=document.getElementById("tabs").getElementsByTagName("li");
var i = liste.length
while(i--) {
if(liste[i].id==2) {
$(liste[i]).detach();
}
}
remove() may be preferred
Keep in mind that if you use .detach(), any jQuery data is retained. If you have no further use for the element, you should be using .remove() instead.
// ...
$(liste[i]).remove(); // clean up all data
code reduction
Finally, since you're using jQuery, you could just do all this in the selector...
$('#tabs li[id=2]').remove(); // or .detach() if needed
valid id attributes
Keep these items in mind with respect to IDs...
It's invalid to have duplicate IDs on a page
It's invalid in HTML4 to have an ID that starts with a number
In the selector above, I used the attribute-equals filter, so it'll work, but you should really be using valid HTML to avoid problems elsewhere.
liste is not (yet) a jQuery object. use $(liste[i])
or use
var liste= $('#tabs li');
Maybe I'm missing something, but is the id suppose to match the number 2.
var liste=document.getElementById("tabs").getElementsByTagName("li");
for(i=0;i<liste.length;i++) {
if(liste[i].id==2) {
$(liste[i]).detach();
}
}
Since you are already using jQuery, why not just do:
$("li", "#tabs").filter("#2").detach();
var two = document.getElementById('2');
two.parentNode.removeChild(two);
How can I reference more then one position of an array?
Something like this
$("#reset").click(function() {
$("form")[0, 2].reset();
});
If you're just using jQuery, you can do this all in a selector.
$("#reset").click(function() {
$("form:eq(0), form:eq(2)").reset();
});
EDIT: Using filter to do this would be better (thanks #Gumbo):
$("#reset").click(function() {
$("form").filter(":eq(0),:eq(2)").reset();
});
A multi-dimensional JavaScript array would be referenced like myArray[i][j] (e.g. myArray[0][2]). Your code example doesn't seem to make sense though.
The simplest way is to just do this:
$("form")[0].reset();
$("form")[2].reset();
or, a little more efficiently:
var forms = $("form");
forms[0].reset();
forms[2].reset();
A nicer way would be to put a common class "myClass" on each of those two forms and do it like this:
$("form.myClass").each(function() {this.reset();});
How about saving your selector to a variable and then looping about indizes?
var form = $('form');
[1, 2].each(function(value, index) {
form[value].reset();
});
I'm writing a jquery-plugin, that changes a css-value of certain elements on certain user-actions.
On other actions the css-value should be reseted to their initial value.
As I found no way to get the initial css-values back, I just created an array that stores all initial values in the beginning.
I did this with:
var initialCSSValue = new Array()
quite in the beginning of my plugin and later, in some kind of setup-loop where all my elements get accessed I used
initialCSSValue[$(this)] = parseInt($(this).css('<CSS-attribute>'));
This works very fine in Firefox.
However, I just found out, that IE (even v8) has problems with accessing the certain value again using
initialCSSValue[$(this)]
somewhere else in the code. I think this is due to the fact, that I use an object ($(this)) as a variable-name.
Is there a way arround this problem?
Thank you
Use $(this).data()
At first I was going to suggest using a combination of the ID and the attribute name, but every object might not have an ID. Instead, use the jQuery Data functions to attach the information directly to the element for easy, unique, access.
Do something like this (Where <CSS-attribute> is replaced with the css attribute name):
$(this).data('initial-<CSS-attribute>', parseInt( $(this).css('<CSS-attribute>') ) );
Then you can access it again like this:
$(this).data('initial-<CSS-attribute>');
Alternate way using data:
In your plugin, you could make a little helper function like this, if you wanted to avoid too much data usage:
var saveCSS = function (el, css_attribute ) {
var data = $(el).data('initial-css');
if(!data) data = {};
data[css_attribute] = $(el).css(css_attribute);
$(el).data('initial-css', data);
}
var readCSS = function (el, css_attribute) {
var data = $(el).data('initial-css');
if(data && data[css_attribute])
return data[css_attribute];
else
return "";
}
Indexing an array with a jQuery object seems fishy. I'd use the ID of the object to key the array.
initialCSSValue[$(this).attr("id")] = parseInt...
Oh please, don't do that... :)
Write some CSS and use the addClass and removeClass - it leaves the styles untouched afterwards.
if anybody wants to see the plugin in action, see it here:
http://www.sj-wien.at/leopoldstadt/zeug/marcel/slidlabel/jsproblem.html