Why do these `element` objects have the `search` property? - javascript

Howdy guys.
I am wondering why does the following code snippet work (tested in the latest Firefox Nightly):
var links = document.querySelectorAll('a[href]');
for (var i = 0; i < links.length; ++i) {
console.log(links[i].search); // Where does `search` come from?
}
As “usual,” I get the query string of the href in each a element (something I can also do with a simple substr or something, but that's not the point); whereas, if I do something like this:
var divs = document.querySelectorAll('div');
for (var i = 0; i < divs.length; ++i) {
console.log(divs[i].search);
}
All I get is undefined.
According to MDN, there is no such thing as search property available for element objects (document.querySelectorAll(selector) returns a non-live NodeList of element objects). So, where does all this come from?
Any help would be greatly appreciated.

Different sorts of HTML element nodes in the DOM have different APIs. The nodes corresponding to <a> tags implement an API for examining URLs. The "search" property is one of those special type-specific things. Basically an <a> node has the same properties as window.location, more or less.

Related

How to get multiple div id using queryselectorAll method

I want to add a functionality on click event.
I want to display none four div but this code seems doesn't work for me
Please tell me the mistake in this
<a class="lightbox-close" href="#" onclick="document.querySelectorAll('#goofy_1,#goofy_2,#goofy_3,#goofy_4').style.display = 'none';"></a>
The querySelectorAll returns a NodeList, you need to iterate over it and set the properties.
So it will be better to write a separate function where you can write the iteration logic
var els = document.querySelectorAll('#goofy_1,#goofy_2,#goofy_3,#goofy_4');
for (var i = 0; i < els.length; i++) {
els[i].style.display = 'none';
}
Query selector is not like jQuery selector where you can do
$('#goofy_1,#goofy_2,#goofy_3,#goofy_4')// it will get you all div selected
Instead Query selector returns nodeList which means you are getting
try console log your querySelectorAll
console.log( querySelectorAll('#goofy_1,#goofy_2,#goofy_3,#goofy_4'));
you will get something like
i.e i am selecting a
now you can see that there isnt single element selected so you can directly make any changes
Now you need to loop through all element like Arun P Johny telling
var allElements = document.querySelectorAll('#goofy_1,#goofy_2,#goofy_3,#goofy_4');
for (var i = 0; i < els.length; i++) {
allElements [i].style.display = 'none';
}
Good Read
Difference between HTMLCollection, NodeLists, and arrays of objects

Trouble Replacing Multiple Links With iFrame Via Javascript

I'm trying to parse a page with javascript to replace links belonging to a specific class with an iframe to open a corresponding wikipedia page [so that rather than having a link you have an embedded result]. The function detects links properly but something about the replaceChild() action causes it to skip the next instance... as if it does the first replace and then thinks the next link is the one it just worked on, probably as a result of the loop.
For example, if there's 2 links on the page, the first will parse and the second will not even be seen but if there's 3, the first two will be parsed using the attributes from the first and third.
Can anyone suggest an alternative way of looping through the links that doesn't rely on a count function? Perhaps adding them to an array?
Sample Links
wiki it
Sample Javascript
(function(){
var lnks = document.getElementsByTagName("a");
for (var i = 0; i < lnks.length; i++) {
lnk = lnks[i]; if(lnk.className == "myspeciallinks"){
newif=document.createElement("iframe");
newif.setAttribute("src",'http://www.wikipedia.com');
newif.style.width="500px";
newif.style.height="100px";
newif.style.border="none";
newif.setAttribute("allowtransparency","true");
lnk.parentNode.replaceChild(newif,lnk);
}
}
})();
The problem here is that document.getElementsByTagName returns a NodeList and not an array. A NodeList is still connected to the actual DOM, you cannot safely iterate over its entries and at the same time remove the entries from the DOM (as you do when you replace the links).
You will need to convert the NodeList into an array and use the array for iteration:
(function(){
var lnksNodeList = document.getElementsByTagName("a");
// create an array from the above NodeList and use for iteration:
var lnks = Array.prototype.slice.call(lnksNodeList);
for (var i = 0; i < lnks.length; i++) {
var lnk = lnks[i];
if (lnk.className == "myspeciallinks") {
var newif = document.createElement("iframe");
newif.setAttribute("src", 'http://www.wikipedia.com');
newif.style.width = "500px";
newif.style.height = "100px";
newif.style.border = "none";
newif.setAttribute("allowtransparency", "true");
lnk.parentNode.replaceChild(newif, lnk);
}
}
})();
According to the MDN documentation:
Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself. The returned list is live, meaning that it updates itself with the DOM tree automatically. Consequently, there is no need to call several times element.getElementsByTagName with the same element and arguments.
Therefore, the collection shrinks every time you replace an a. You could change your loop to decrement i whenever you do a replace.

How do i display various div values using divs with the same id?

Ok to start of i currently have 4 divs with the same id for example:
<div id='name'></div>
<div id='name'></div>
<div id='name'></div>
<div id='name'></div>
and im currently using a javascript function to display the value of the div's for example:
function divCheck(){
alert(document.getElementById('name').innerHTML);
}
the problem im having is when i call the function it only displays the value of the first div.
My goal is to display the values of all the divs and place it into a Textarea input.
I will really truly appreciate it. In advance thank you.
Use class instead of id, and use getElementsByClassName
ID is for use once, and is generally for large item (div etc) which has to be pretty unique, or is to be individually accessed, when you need to access combinations or even apply CSS properties on grpups of html elements without having to type them again and for each id, use class, and apply the common properties to that class, use ID for unique properties.
Similarly here use class, as you can see the function is get*Elements*ByClassName, means it returns a group, and this is what class is for. For this kind of use, use class instead of ID.
As others have said, use classes instead of ids. Each id must be unique. You cannot have more than one object with the same id. Here's how it looks with a class name instead:
<div class='name'></div>
<div class='name'></div>
<div class='name'></div>
<div class='name'></div>
And, here's how you get all objects with a given class name and iterate over them:
function divCheck() {
var elems = document.getElementsByClassName('name');
for (var i = 0; i < elems.length; i++) {
alert(elems[i].innerHTML);
}
}
Unfortunately, getElementsByClassName() was not supported by IE until IE9 so you will have to use a javascript shim that implements it a different way when it doesn't already exist. Or, use a pre-built library like Sizzle or jQuery that already support this type of functionality in older browsers.
Hey why don't you use the class instead of Ids. Give dynamic classNames like class="className-'+id+'"
And the call them using :
$('div[class^="className-'+id+'""]')
Hope it will be useful.
P.S Avoid using same ids for elements.
Since you seem to be after a getElementsByClassName function independant of any library, try the following. It tries querySelectorAll first, if not available it tries getElementsByClassName, and finally does an old school iterate over elements approach.
It will also accept multiple class names and always returns a static NodeList or array of the matched elements (per querySelectorAll). Note that getElemensByClassName returns a live NodeList, so the result must be converted to an array otherwise it might behave a little differently if elements are being added or removed from the document.
/*
Selector must be per CSS period notation, using attribute notation
(i.e. [class~=cName]) won't work for non qSA browsers:
single class: .cName
multiple class: .cName0.cName1.cName2
If no root element provided, use document
First tries querySelectorAll,
If not available replaces periods '.' with spaces
and tries host getElementsByClassName
If not available, splits on spaces, builds a RegExp
for each class name, gets every element inside the
root and tests for each class.
Could remove duplicate class names for last method but
unlikely to occur so probably a waste of time.
Tested in:
Firefox 5.0 (qSA, gEBCN, old)
IE 8 (old method only, doesn't support qSA or gEBCN)
Chrome 14 (qSA, gEBCN, old)
*/
function getByClassName(cName, root) {
root = root || document;
var reClasses = [], classMatch;
var set = [], node, nodes;
// Use qSA if available, returns a static list
if (root.querySelectorAll) {
return root.querySelectorAll(cName);
}
// Replace '.' in selector with spaces and trim
// leading and trailing whitespace for following methods
cName = cName.replace(/\./g, ' ').replace(/^\s+/,'').replace(/\s+$/,'');
// Use gEBCN if available
if (root.getElementsByClassName) {
nodes = root.getElementsByClassName(cName);
// gEBCN usually returns a live list, make it static to be
// consistent with other methods
for (var i=0, iLen=nodes.length; i<iLen; i++) {
set[i] = nodes[i];
}
return set;
}
// Do it the long way... trim leading space also
nodes = root.getElementsByTagName('*');
cName = cName.split(/\s+/);
// Create a RegExp array of the class names to search on
// Could filter for dupes but unlikely to be worth it
for (var j = 0, jLen = cName.length; j < jLen; j++) {
reClasses[j] = new RegExp('(^|\\s+)' + cName[j] + '\\s+|$');
}
// Test each element for each class name
for (var m = 0, mLen = nodes.length; m < mLen; m++) {
node = nodes[m];
classMatch = true;
// Stop testing class names when get a match
for (var n = 0, nLen = reClasses.length; n < nLen && classMatch; n++) {
classMatch = node.className && reClasses[n].test(node.className);
}
if (classMatch) {
set.push(node);
}
}
return set;
}

Beginner Javascript error with getAttribute

I've searched quite a bit on both google and stackoverflow, but a lack of knowledge on how to ask the question (or even if I'm asking the right question at all) is making it hard to find pertinent information.
I have a simple block of code that I am experimenting with to teach myself javascript.
var studio = document.getElementById('studio');
var contact = document.getElementById('contact');
var nav = document.getElementById('nav');
var navLinks = nav.getElementsByTagName('a');
var title = navLinks.getAttribute('title');
I want to grab the title attribute from the links in the element with the ID 'nav'.
Whenever I look at the debugger, it tells me that Object #<NodeList> has no method 'getAttribute'
I have no idea where I'm going wrong.
The nodetype and nodevalue for navLinks comes back as undefined, which I believe may be part of the problem, but I'm so new to this that I honestly have no idea.
The getElementsByTagName method returns an array of objects. So you need to loop through this array in order to get individual elements and their attributes:
var navLinks = nav.getElementsByTagName('a');
for (var i = 0; i < navLinks.length; i++) {
var link = navLinks[i];
var title = link.title;
}
Calling nav.getElementsByTagName('a') returns list of objects. And that list doesn't have getAttribute() method. You must call it on ONE object.
When you do:
navLinks[0].getAttribute('title')
then it should work - you will get title of the first matched element.
var navLinks = nav.getElementsByTagName('a');
getElementsByTagName returns multiple elements (hence Elements), because there can be multiple elements on one page with the same tag name. A NodeList (which is a collection of nodes as returned by getElementsByTagName) does not have a getAttribute method.
You need to access the property of the element that you actually need. My guess is that this will be the first element you find.
var title = navLinks[0].getAttribute('title');

getElementsByName in IE7

I have some code doing this :
var changes = document.getElementsByName(from);
for (var c=0; c<changes.length; c++) {
var ch = changes[c];
var current = new String(ch.innerHTML);
etc.
}
This works fine in FF and Chrome but not in IE7. Presumably because getElementsByName isn't working in IE. What's the best workaround?
In case you don't know why this isn't working in IE, here is the MSDN documentation on that function:
When you use the getElementsByName method, all elements in the document that have the specified NAME attribute or ID attribute value are returned.
Elements that support both the NAME attribute and the ID attribute are included in the collection returned by the getElementsByName method, but elements with a NAME expando are not included in the collection; therefore, this method cannot be used to retrieve custom tags by name.
Firefox allows getElementsByName() to retrieve elements that use a NAME expando, which is why it works. Whether or not that is a Good Thing™ may be up for debate, but that is the reality of it.
So, one option is to use the getAttribute() DOM method to ask for the NAME attribute and then test the value to see if it is what you want, and if so, add it to an array. This would require, however, that you iterate over all of the nodes in the page or at least within a subsection, which wouldn't be the most efficient. You could constrain that list beforehand by using something like getElementsByTagName() perhaps.
Another way to do this, if you are in control of the HTML of the page, is to give all of the elements of interest an Id that varies only by number, e.g.:
<div id="Change0">...</div>
<div id="Change1">...</div>
<div id="Change2">...</div>
<div id="Change3">...</div>
And then have JavaScript like this:
// assumes consecutive numbering, starting at 0
function getElementsByModifiedId(baseIdentifier) {
var allWantedElements = [];
var idMod = 0;
while(document.getElementById(baseIdentifier + idMod)) { // will stop when it can't find any more
allWantedElements.push(document.getElementById(baseIdentifier + idMod++));
}
return allWantedElements;
}
// call it like so:
var changes = getElementsByModifiedId("Change");
That is a hack, of course, but it would do the job you need and not be too inefficient compare to some other hacks.
If you are using a JavaScript framework/toolkit of some kind, you options are much better, but I don't have time to get into those specifics unless you indicate you are using one. Personally, I don't know how people live without one, they save so much time, effort and frustration that you can't afford not to use one.
There are a couple of problems:
IE is indeed confusing id="" with name=""
name="" isn't allowed on <span>
To fix, I suggest:
Change all the name="" to class=""
Change your code like this:
-
var changes = document.getElementById('text').getElementsByTagName('span');
for (var c=0; c<changes.length; c++) {
var ch = changes[c];
if (ch.className != from)
continue;
var current = new String(ch.innerHTML);
It's not very common to find elements using the NAME property. I would recommend switching to the ID property.
You can however find elements with a specific name using jQuery:
$("*[name='whatevernameYouWant']");
this will return all elements with the given name.
getElementsByName is supported in IE, but there are bugs. In particular it returns elements whose ‘id’ match the given value, as well as ‘name’. Can't tell if that's the problem you're having without a bit more context, code and actual error messages though.
In general, getElementsByName is probably best avoided, because the ‘name’ attribute in HTML has several overlapping purposes which can confuse. Using getElementById is much more reliable. When specifically working with form fields, you can more reliably use form.elements[name] to retrieve the fields you're looking for.
I've had success using a wrapper to return an array of the elements. Works in IE 6, and 7 too. Keep in mind it's not 100% the exact same thing as document.getElementsByName, since it's not a NodeList. But for what I need it for, which is to just run a for loop on an array of elements to do simple things like setting .disabled = true, it works well enough.
Even though this function still uses getElementsByName, it works if used this way. See for yourself.
function getElementsByNameWrapper(name) {
a = new Array();
for (var i = 0; i < document.getElementsByName(name).length; ++i) {
a.push(document.getElementsByName(name)[i]);
}
return a;
}
Workaround
var listOfElements = document.getElementsByName('aName'); // Replace aName with the name you're looking for
// IE hack, because it doesn't properly support getElementsByName
if (listOfElements.length == 0) { // If IE, which hasn't returned any elements
var listOfElements = [];
var spanList = document.getElementsByTagName('*'); // If all the elements are the same type of tag, enter it here (e.g.: SPAN)
for(var i = 0; i < spanList.length; i++) {
if(spanList[i].getAttribute('name') == 'aName') {
listOfElements.push(spanList[i]);
}
}
}
Just another DOM bug in IE:
Bug 1: Click here
Bug 2: Click here

Categories