I wish to change the name attribute at dynamic in JavaScript, but I couldn't change the name attribute using either element.name or the setAttribute() method. Is there any other way to set the name attribute?
var status_Element = document.getElementsByName("BD_task_status_"+old_id+"[]");
var final_status_Element = document.getElementsByName("BD_final_task_status_"+old_id+"[]");
for(var in_count=0;in_count<status_Element.length;in_count++)
{
status_Element[in_count].name="BD_task_status_level"+inner_count+"[]";
final_status_Element[in_count].name="BD_final_task_status_level"+inner_count+"[]";
}
Setting the name property as in your example will work (other than the inner_count vs in_count typo). The issue is that getElementsByName returns a live collection of elements. When you change the name of the element at index 0, it no longer belongs in the collection and the collection drops it; then the element that used to be at index 1 is now at index 0. But in the meantime, you've incremented in_count and so you never end up changing that element. Half of them will end up not being changed.
Either:
Loop backward through the collection to change the names (since then the element that disappears is at the end and you don't care), or
Use querySelectorAll instead, which gives you a snapshot collection (not a live one). (querySelectorAll is supported on all modern browsers, and also IE8.)
Related
I have this scenario where i need to assign a java-script object to div attribute.
var temp["name"]="first";
i assigned it to a div tag attribute using Jquery.
div.attr('data-polygon',temp);
Object [ <div#image01> ]
when i try to retrieve it , it does not come back same.. it is converted as String.
div.attr('data-polygon');
"[object Object]"
Any proper option / alternate to retain the object in attribute? please let me know
Attributes are always strings.
Here are two options for doing this; since you're already using jQuery, the first is probably best:
jQuery's data
jQuery provides the data method for doing this:
// Setting -- notice no "data-" prefix
div.data("polygon", temp);
// Retrieving
varName = div.data("polygon");
Under the covers, traditionally data assigns the element a unique ID and manages a data cache for the element, separate from it. I think in the v3 branch they've updated it to actually store the data on the element (there were memory issues with doing that in Internet Explorer 8 and earlier, which is why jQuery didn't do it before).
Assign it as your own "expando" property
If you don't need to support Internet Explorer 8, you can just create a property on the actual DOM element.
// Setting
rawDiv.myUniquePrefixToAvoidConflicts_polygon = temp;
// Getting
varName = rawDiv.myUniquePrefixToAvoidConflicts_polygon;
Note that to avoid conflicts with the many predefined properties elements have, and future ones they don't have yet, you'll want to use a likely-unique prefix.
Is there a way to get/select an element by it's property value, just as it is possible with the attribute values:
$('[attribute="value"]')
For example, I'd set a property using jQuery like this:
$('#foo').prop('my-property', 'value');
and then I'd like to find an element which has property 'my-property' and it has value 'value'.
No, there isn't anything exposed at the selector level that can select by property value, just (as you know) attribute value.
Some properties are reflections of attributes, which means that setting the property sets the attribute, which allows you to use attribute selectors. For instance, an input element's defaultValue property is a reflection of its value attribute (which its value property is not).
Otherwise, you select by what you can and use filter to filter the resulting list to only the elements you actually want.
Re your edit:
For example, I'd set a property using jQuery like this:
$('#foo').prop('my-property', 'value');
No, there's no way to select by that property directly, you'd need something like my filter suggestion above:
var list = $("something-that-gets-you-close").filter(function() {
return this["my-property"] == "value";
});
You might consider using data-* attributes instead:
$("#foo").attr("data-my-property", "value");
then
var list = $("[data-my-property='value']");
to select it (the inner quotes are optional for values matching the definition of a CSS identifier). Note that attribute values are always strings.
Beware: There's a persistent misconception that jQuery's data function is a simple accessor for data-* attributes. It is not. It manages a data cache associated with the element by jQuery, which is initialized from data-* attributes but disconnected from them. In particular, .data("my-property", "value") would not let you find that later via a [data-my-property=value] selector.
I have a webshop and with jquery i need to check when adding a product if it exists in cart, then it should increase quantity instead of creating a new row.
The code i use to check if it exists in cart is the following:
if ($(".store-purchase-list-quantity-plus").attr('part') != SalesNO)
If this is true, it should add a new row. If its false it should add quantity.
SalesNO is the product id.
This works only for the first product in cart, then it just keeps adding a new row. It seems to me the if statement only checks the first ".store-purchase-list-quantity-plus", even if there are multiple of them in the DOM.
How do i change my if statement so it checks all .store-purchase-list-quantity-plus" after (attr = salesNO)
It's working exactly as it should. That's just not how you expect it to work.
From the documentation:
The .attr() method gets the attribute value for only the first element in the matched set. To get the value for each element individually, use a looping construct such as jQuery's .each() or .map() method.
There are a number of looping constructs available in jQuery, though given your particular need - finding out if there's an element that has a specific attribute value in a set of matched elements - using .filter() is probably the best choice.
var partExists = $(".store-purchase-list-quantity-plus").filter(function() {
return $(this).attr('part') == SalesNO;
}).length > 0; // there's an element with a part attribute matching SalesNO
if(partExists) {
// increase quantity
}
else {
// add new entry
}
if there are more matches on the page with class store-purchase-list-quantity-plus, you will still only select the first with $(".store-purchase-list-quantity-plus").
Make a loop to check all.
If I have <element id="blah" class="blah" attr="blah"> (edit: and I have NO other elements in the DOM, just this one)
Then are these jQuery objects absolutely equivalent:
$("#blah")
$(".blah")
$("[attr=blah]")
Is it the case that the query is executed when the object is created and after that it is irrelevant what query was used?
Edit: Does the original query used have any impact later on anything I might do with the resulting jQuery object? Are the 3 resulting objects above identical? Are they any lasting effects from the actual query I had used? (e.g., when I do something with that object later)
Each may get the element, however they do it in different ways because they are different selectors.
The id selector #
$("#blah")
This will return a jQuery object with 1 element in it (the element you list). The benefit of using an id is that it will only return your one element if it exists, and is fastest as a result of ids being expected to be unique.
The class selector .
$('.blah')
This will return a jQuery object with an array of elements in it (including the element you lsit), but also with any other element that has this class. Since there is no combination with this selector, it will be slower than a straight id lookup because it must inspect every element on the page for this class.
The attribute selector []
$("[attr=blah]")
Much like the class selector, this will return an array of elements. It also must inspect every element.
These may look the same if there is only one match when a jQuery function call is used. The reason for that happening is jQuery will look to see if there is an array of elements matched, and then internally use $.each on the set to apply the jQuery function call to them. The benefit is that this makes sets of elements responds very similarly to single elements which are wrapped by the jQuery object.
Here is a whole list of selectors jQuery supports:
http://api.jquery.com/category/selectors/
The element will match all three selectors, but it does not make the three selectors absolutely equivalent because they all have different matching criteria (one looks for an ID, one looks for a class name, and one looks for an arbitrary attribute). In particular, the class and attribute selectors can return more than one element, since unlike an ID selector they do not imply uniqueness of an element.
Even if you can guarantee that this element will be the only one matched by all three selectors, every call to $ always yields a unique jQuery object, even if the resulting jQuery objects encapsulate the same DOM element.
The query returns an array of html elements. In this specific case all queries return the same array. There is no way the original query will have an effect later.
I'm playing around with getting attributes of a link tag, there seems to be several ways of accessing the attributes:
document.getElementsByTagName("link")[0]['media']
document.getElementsByTagName("link")[0].media
document.getElementsByTagName("link")[0].getAttribute('media')
document.getElementsByTagName("link")[0].attributes['media']
It's bordering on ridiculous how many paths there are to the same data. Is one of these methods far superior to the rest?
I would use .media for this case, since media is indeed a property on the link element. Each of these has its use:
['media']: Retrieves the "media" property value using square bracket notation. Use square bracket notation when you don't know the name of the property at design time. For example, when iterating properties.
.media: Retrieves the "media" property value. I'd use this in most cases. It provides concise, direct access to the property value.
.getAttribute('media'): Retrieves the "media" attribute value. Use this when you want the value of an attribute that is not necessarily a property on the element. Not all attributes are properties and not all properties are attributes.
.attributes['media']: Retrieves the "media" attribute node. Use the attributes collection when you need more information about an attribute than just it's value. For example, the attribute name. You can also easily use this to get the value, since .toString() returns the value, but that may be overkill if all you want is the value. The attributes collection is also useful for iterating the attributes of an element.
The method you are looking for is called getElementsByTagName. It returns an array-like list of elements (which is not an array).
Note that your last sample .attributes['media'] does not return a string as the other methods. It returns an attribute node instead.
In theory the ways of accessing the content should be equivalent but browser bugs led to other behavior in reality. It's probably best to use an abstraction layer (a library such as jQuery) to get consistent behavior. If you intend to program without a library the choice depends on your taste however I'd say that going via the attribute node is safest in general.
To add a bit more technical detail, although the different way return the same ways most of the time this is not necessarily true for non-existent attributes. Take the following HTML as example: <a href='test'>. You can try it yourself in another browser on a test jsFiddle (the output below is from Firefox).
// Get reference to element
var a = document.getElementsByTagName('a')[0];
// Existent attributes
console.log(a.href); // String: http://fiddle.jshell.net/_display/test
console.log(a.getAttribute('href')); // String: test
console.log(a.attributes['href']); // Attribute node: href
Note that one time an absolute URI was returned, another time the original value was returned.
// Existent invalid attributes
console.log(a.other); // undefined
console.log(a.getAttribute('other')); // String: thing
console.log(a.attributes['other']); // Attribute node: other
Everything that exists on page load gets merged into DOM but is not available as property if invalid.
// Inexistent but valid attributes
console.log(a.title); // Empty string
console.log(a.getAttribute('title')); // null
console.log(a.attributes['title']); // undefined
The first call returned a properties default value. Then we saw null as a marker for an inexistent attribute. Lastly we got a so called NamedNodeMap which is something like a mixture of an array and object. Accessing it as an object gave the undefined value.
// Creating attributes
a.setAttribute('title', 'test title');
console.log(a.title); // String: test title
console.log(a.getAttribute('title')); // String: test title
console.log(a.attributes['title']); // Attribute node: title
Attribute becomes available as property, too.
// Creating "attributes" by using property
a.rel = 'test rel';
console.log(a.rel); // String: test rel
console.log(a.getAttribute('rel')); // String: test rel
console.log(a.attributes['rel']); // Attribute node: rel
Setting property for a valid attribute also creates an entry in attributes map.
// Inexistent invalid attributes
console.log(a.dummyInvention); // undefined
console.log(a.getAttribute('dummyInvention')); // null
console.log(a.attributes['dummyInvention']); // undefined
Property access on a, marker return value and index access on node map.
// Creating invalid attributes via setAttribute
a.setAttribute('title2', 'test title2');
console.log(a.title2); // undefined
console.log(a.getAttribute('title2')); // String: test title2
console.log(a.attributes['title2']); // Attribute node: title2
Attribute gets created even though its existent is invalid but it is not available as property.
// Creating invalid "attributes" via property
a.title3 = 'test title3';
console.log(a.title3); // String: test title3
console.log(a.getAttribute('title3')); // null
console.log(a.attributes['title3']); // undefined
Object a is extended but DOM is untouched.
// NamedNodeMap of length 4 and indexes other, href, title, rel, title2 (valid attributes or result of setAttribute in order of creation except those from parsing)
console.log(a.attributes);
The node map only reflects the current state of the DOM. It is not aware of extension to our object a that we received via getElementsByTagName.
It's important to note that manipulating JavaScript object does not necessarily affect the DOM. The DOM only reflects the things that have been available on parsing plus modification with DOM methods or property modifications (of predefined properties that is). I hope I did not miss any important cases and that the comments have been verbose enough to see what happens.
I would appreciate a comment on the final NamedNodeMap because I would like to know if Firefox's behavior is correct there to discard the order of the attributes from parsing.
Functionally, they are equal.
Performance-wise, the first two are superior by a significant factor - though they are all extremely fast. See this JSPerf test.
Practically speaking, the first two are easier to read, and my personal preference is the second. (It's also a hair faster.)
The first two options are the the same. You can use either. I personally prefer the .media version since I think it reads easier.
The last two options depend upon getAttribute() and setAttribute() which have not always been reliable in IE. You can read a lot more about that in the reference that Matt posted. As such, I prefer the .media version out of all four of your choices as most reliable and most readable.