I've recently discovered a very fundamental difference between the 2 methods of setting custom DOM attributes in Javascript. The difference is in how the HTML5 Selectors API interacts with those attributes (ie, in document.querySelector() and friends).
<button id="b3">View</button>
<script>
document.getElementById('b3').shape = 'square';
console.log( document.querySelector('*[shape]') ); // FAIL: returns null
document.getElementById('b3').setAttribute('shape','square');
console.log( document.querySelector('*[shape]') ); // WORKS: returns element
</script>
So basically if you apply attributes to an element without using .setAttribute() then you cannot later select the element by the attribute name.
BTW. This behaviour is consistent across browsers which makes me think it might be addressed by the standard, however I can't see it:
http://www.w3.org/TR/css3-selectors/#attribute-selectors
http://www.w3.org/TR/selectors-api/
The selectors API standard doesn't appear to care:
Selectors allow the representation of an element's attributes. When a
selector is used as an expression to match against an element,
attribute selectors must be considered to match an element if that
element has an attribute that matches the attribute represented by the
attribute selector.
The entire attribute matching rule seems to boil down to "if the element has an attribute" and you would think that someElement.someAttribute = something would meet the criteria of "having an attribute" but for whatever reason it doesn't.
My question is basically why the difference? Is it actually part of a standard or just an identical implementation quirk in all the major browsers (IE11, FF38 and Chrome 43)?
The reason is very simple - getElementById and all of those kind return an Element Object (see specs: http://www.w3schools.com/jsref/met_document_getelementbyid.asp)
Which means you set property shape on an object. Then you try to do a query selector, but you haven't modified the html that's queried. They are different things.
Related
I have, the div's where id looks like this_div_id_NUMBER, all div's has the different NUMBER part. How I find all div's just using this_div_id part of id ?
you can use querySelectorAll to hit partial attribs, including ID:
document.querySelectorAll("[id^='this_div_id']")
the ^ next to the equal sign indicates "starts with", you can use * instead, but it's prone to false-positives.
you also want to make sure to use quotes (or apos) around the comapare value in attrib selectors for maximum compatibility on querySelectorAll; in jQuery and evergreen browsers it doesn't matter, but in vanilla for old browsers it does matter.
EDIT: late breaking requirement needs a more specific selector:
document.querySelectorAll("[id^='this_div_id']:not([id$='_test_field'])");
the not() segment prevents anything ending with "_test_field" from matching.
proof of concept / demo: http://pagedemos.com/partialmatch/
querySelectorAll
querySelectorAll takes CSS selectors and returns a HTMLNodeList (a kind of array) of all the elements matching that css selector.
The css selector ^ (starts with) can be used for your purpose. Learn more about it in this article.
For your case, it would be document.querySelectorAll("[id^='this_div_id']");
Note that querySelectorAll doesn't return a live node list. That means, any changes to the dom would not be updated live in the return value of the function.
Another method would to assign all your elements a specific class and then you can use getElementsByClassName (which is much faster than querySelector).
var divs = document.getElementsByClassName("divClass");
Try this selector:
[id^="this_div_id_"]
Pure JavaScript: (reference)
document.querySelectorAll('[id^="this_div_id_"]')
jQuery: (reference)
$('[id^="this_div_id_"]')
CSS: (reference)
[id^="this_div_id_"] {
/* do your stuff */
}
Why is this working?
With the [] in the selector, you can select attributes. Use '=' to match exactly the value or use the '^=' to check if the value starts with. Read more about this here.
Using attribute selectors:
div[id^="this_div_id"]
It is better to use classes.
But there is one solution that you need (assuming you use jQuery):
$("*[id^='this_div_id']")
Just using attribute selector like below:
$("[id^='this_div_id_']")
Dear StackOverflow community,
I am new to jQuery and Javascript, and was wondering what the main differences between these two functions are:
document.getElementById('id').innerHTML =variable;
and
jQuery('#id').val(variable);
From my understanding, they are two different coding techniques, but when would I use one over the other? and why?
The two above examples are slightly different, but not in the way you're expecting.
innerHTML and .val() are not equivalent methods.
jQuery will try to use querySelector / querySelectorAll where appropriate when using the DOM selection jQuery("SELECTOR"). These are native methods, so look into them.
.val(variable) will set the value of the node found by jQuery("SELECTOR") to variable
innerHTML = variable will set the the HTML content of document.getElementById('id') to whatever variable is.
.val() - Set the value of each element in the set of matched elements.
innerHTML - innerHTML sets or gets the HTML syntax describing the element's descendants.
Given:
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<a xlink:href="url"></a>
</svg>
</body>
Is it possible to use the HTML DOM's .querySelector() or .querySelectorAll() to select the link inside the SVG by the contents of its xlink:href attribute?
This works:
document.querySelector('a') // <a xlink:href="url"/>
These don't:
document.querySelector('[href="url"]') // null
document.querySelector('[xlink:href="url"]') // Error: not a valid selector
document.querySelector('[xlink\:href="url"]') // Error: not a valid selector
document.querySelector('[xlink\\:href="url"]') // null
Is there a way of writing that attribute selector to make it 'see' the xlink:href?
Query selector can handle namespaces, but it gets tricky because
The syntax for specifying namespaces in CSS selectors is different from html;
The querySelector API doesn't have any method for assigning a namespace prefix (like xlink) to an actual namespace (like "http://www.w3.org/1999/xlink").
On the first point, the relevant part of the CSS specs allows you to specify no namespace (the default), a specific namespace, or any namespace:
#namespace foo "http://www.example.com";
[foo|att=val] { color: blue }
[*|att] { color: yellow }
[|att] { color: green }
[att] { color: green }
The first rule will match only elements with the attribute att in the "http://www.example.com" namespace with the value "val".
The second rule will match only elements with the attribute att regardless of the namespace of the attribute (including no namespace).
The last two rules are equivalent and will match only elements with the attribute att where the attribute is not in a namespace.
See this fiddle, paying attention to the fill styles (default, hover, and active):
https://jsfiddle.net/eg43L/
The Selectors API adopts the CSS selector syntax, but has no equivalent to the #namespace rule for defining a namespace. As a result, selectors with namespaces are not valid but the wildcard namespace token is valid:
If the group of selectors include namespace prefixes that need to be resolved, the implementation must raise a SYNTAX_ERR exception ([DOM-LEVEL-3-CORE], section 1.4).
This specification does not provide support for resolving arbitrary namespace prefixes. However, support for a namespace prefix resolution mechanism may be considered for inclusion in a future version of this specification.
A namespace prefix needs to be resolved if the namespace component is neither empty (e.g. |div), representing the null namespace, or an asterisk (e.g. *|div), representing any namespace. Since the asterisk or empty namespace prefix do not need to be resolved, implementations that support the namespace syntax in Selectors must support these.
(bold added)
Check out the fiddle again, this time paying attention to the console output. The command document.querySelector('[*|href="#url"]') returns the element you want.
One final warning: MDN tells me that IE8- do not support CSS namespaces, so this might not work for them.
Update 2015-01-31:
As #Netsi1964 pointed out in the comments, this doesn't work for custom namespaced attributes in HTML 5 documents, since HTML doesn't support XML namespaces. (It would work in a stand-alone SVG or other XML document including XHTML.)
When the HTML5 parser encounters an attribute like data:myAttribute="value" it treats that as a single string for the attribute name, including the :. To make things more confusing, it auto-lowercases the string.
To get querySelector to select these attributes, you have to include the data: as part of the attribute string. However, since the : has special meaning in CSS selectors, you need to escape it with a \ character. And since you need the \ to get passed through as part of the selector, you need to escape it in your JavaScript.
The successful call therefore looks like:
document.querySelector('[data\\:myattribute="value"]');
To make things a little more logical, I would recommend using all lower-case for your attribute names, since the HTML 5 parser will convert them anyway. Blink/Webkit browser will auto-lowercase selectors you pass querySelector, but that's actually a very problematic bug (in means you can never select SVG elements with mixed-case tag names).
But does the same solution work for xlink:href? No! The HTML 5 parser recognizes xlink:href in SVG markup, and correctly parses it as a namespaced attribute.
Here's the updated fiddle with additional tests. Again, look at the console output to see the results. Tested in Chrome 40, Firefox 35, and IE 11; the only difference in behavior is that Chrome matches the mixed-case selector.
[*|href] will match both html href and svg xlink:href, then use :not([href]) to exclude html href.
document.querySelectorAll('[*|href]:not([href])')
tested in chrome
Unfortunately not.
querySelector doesn't handle XML namespaces, so there is no easy way to do this that way. You can however use an XPath query.
var result = document.evaluate(
// Search for all nodes with an href attribute in the xlink namespace.
'//*[#xlink:href="url"]',
document,
function(prefix){
return {
xlink: "http://www.w3.org/1999/xlink"
}[prefix] || null;
},
XPathResult.ORDERED_NODE_ITERATOR_TYPE
);
var element = result.iterateNext();
If you need full cross-browser support, such as for IE, which does not have a document.evaluate, you can polyfill it with wicked-good-xpath.
Of course, depending on your usage, it may be easier to do this (which I think will work on IE):
var element = Array.prototype.filter.call(document.querySelectorAll('a'),
function(el){
return el.getAttributeNS('http://www.w3.org/1999/xlink', 'href') === 'url';
})[0] || null;
It appears that sometimes object.setAttribute(attrib,value) isn't equivalent to object.attrib=value in javascript?
I've got the following code, which works fine:
var lastMonthBn = document.createElement('input');
lastMonthBn.value='<'; // This works fine
lastMonthBn.type='button'; // This works fine
But the following code doesn't:
var div = document.createElement('div');
div.class = 'datepickerdropdown'; // No luck here!
So i need to use the following:
div.setAttribute('class','datepickerdropdown');
My question is, why? From reading this, I thought that object.setAttribute(blah,value) was the same as object.blah=value??
Properties and Attributes aren't really the same, however the DOM exposes standard attributes through properties.
The problem you're facing specifically with the class attribute is that class is a future reserved word.
In some implementations the use of a future reserved word can cause a SyntaxError exception.
For that reason, the HTMLElement DOM interface provides a way to access the class attribute, through the className property:
var div = document.createElement('div');
div.className = 'datepickerdropdown';
Remember, attributes aren't the same as properties, for example:
Immagine a DOM element that looks like this:
<div></div>
If you add a custom attribute to it, e.g.:
myDiv.setAttribute('attr', 'test');
An attribute will be added to the element:
<div attr="test"></div>
Accessing attr as a property on the div element, will simply give you undefined (since is not a property).
myDiv.foo; // undefined
If you bind a property to an element, e.g.:
myDiv.prop = "test";
The getAttribute method will not be able to find it, (since is not an attribute):
myDiv.getAttribute('test'); // null
Note: IE wrongly messes up attributes and properties. :(
As I've said before, the DOM exposes standard attributes as properties, but there are some exceptions that you'll have to know:
The class attribute, is accessible through the className property (the problem you have).
The for attribute of LABEL elements, is accessible through the htmlFor property (collides with the for statement).
Attributes are case-insensitive, but the language bindings for JavaScript properties are not, so the convention is to use the names is camelCase to access attributes through properties, for example the ones formed by two words, e.g. cellSpacing, colSpan, rowSpan, tabIndex, maxLength, readOnly frameBorder, useMap.
It should be noted that browsers like Safari will NOT run JavaScript if keywords like "class" or "int" are present.
So it's a cross-browser support sort of thing. "class" is present in JS2.0 [I believe a package system is available there too]
...
I should also note that in IE, setAttribute [for non-class things, since setAttribute should be use-able for other members such as "style"] can be glitchy.
Besides the ID, if you say you want a unique identifier for an HTML element (let’s say a div).
I browsed the DOM for something (like a number or string) that was unique for each element; but the DOM was big and I failed to find that on the Internet.
Is there a property (in the DOM obviously) that is unique only to that element? (Other than the ID and also you don't specify it, but it comes when the DOM is constructed.)
Depending on the objective, here are two suggestions.
Unless you actually need to express the id as some kind of string, you can save the normal DOM reference.
If you do need to express it as a string for some reason, then you'll need to assign a unique id yourself.
var getId = (function () {
var incrementingId = 0;
return function(element) {
if (!element.id) {
element.id = "id_" + incrementingId++;
// Possibly add a check if this ID really is unique
}
return element.id;
};
}());
The only other identifier I can think of is the XPath of the element in the document.
For instance, the title link inside the heading of this very page has an XPath of
/html/body/div[3]/div[2]/div/h1/a
But like Pekka already said, it depends on what you want to do. And I don’t think you can get the XPath easily from the DOM in JavaScript, despite XPath being available nowadays in JavaScript engines.
Internet Explorer has a property, "uniqueID", for every element. The problem is that the other browsers don't support it.
You can use a library or roll your own to create a unique identifier. jQuery has .data():
Store arbitrary data associated with the matched elements or return
the value at the named data store for the first element in the set of
matched elements.
I just encountered the same situation, and while I was looking into some DOM elements in the Chrome developer tools inspector, I noticed that they all seem to have a property like jQuery11230892710877873282 assigned with a unique number.
Obviously the number after 'jQuery' is different every time you load the page. My guess is that jQuery is generating this internally every time it tries to access or manipulate any DOM element.
I played a little bit with it, and it looks like elements that are never accessed/manipulated by jQuery may not have this property, but the moment you do something like $(elem), the property will be there. So, since we're using both jQuery and Lodash, I devised the following function to return a unique ID regardless of whether the element actually has a DOM id attribute.
_.reduce(
$(elem),
function(result, value, key) {
if(_.startsWith(key, 'jQuery'))
return value;
},
0)
There is the name attribute that can be addressed by document.getElementByName.
I don't think other unique identifiers exist - even though you could simulate one by setting a property (like title) to a unique value, and then query for that. But that is kludgy.