How to select multiple ID's in a badly designed webpage? - javascript

I am maintaining a complex web application.
I have a large number of divs which all have the same ID.
I know this is totally wrong, and as a matter of fact document.getElementById() with that id is going to only produce one match for me.
However I am able to pull out the element that I'm looking for using jQuery (we are on 1.6.2), like this: $('#bad_id[nonstandard_attr_name=somethingSpecific]')
Not quite ready to say that this is a "solution".
I'm worried about whether this is reliable or not. Is jQuery really actually gonna search through all the elements that match the ID using a DOM walk? That's probably the only way to get all of them.
Does it filter elements by the other attribute first, and then filter it down by the ID? That would achieve the desired behavior as well, but it would be good to know the order it does this in.

If you need to select multiple elements with same id you can simply use an attribute selector:
$( "[id='myid']" )
The attribute selector doesn't look at the attribute key for any semantics like unique ids or such.
http://jsfiddle.net/ZWm3G/

I cannot tell you what jQuery or other dom traversals will do (doubtful it will work always) but you can try this :
document.filter = function(attr, val, r) {
r = r || document.getElementsByTagName("*");
var s = [];
for(var i = 0; i < r.length; i++) {
if(r[i].getAttribute(attr) == val) {
s.push(r[i]);
}
}
return s;
};
var s = document.filter("nonstandard_attr_name", "somethingSpecific", document.filter("id", "bad_id"));
console.log(s);
http://jsfiddle.net/KGPFf/1/

Well, as seen in comments, my guess was why would it go on searching for any element. Yes
getElementById returns the first element and then stops searching, but the same doesn't look right for jQuery. It does return all the elements with that "bad" id.
As can be seen in this Fiddle,
So it is selecting all the elements, this means jQuery doesn't stop at the first element, but goes on searching the entire doument, hence IMO, you may use jQuery to select multiple elements with the common id. There shouldn't be any problem.

Use a selector that will result in something other than getElementById() being used should result in consistent results, but make sure you test it with IE8 since IE8 doesn't use document.querySelectorAll().
Using methods such as .find .children and .filter should also yield consistent results regardless of the id being unique.
Sample: http://jsfiddle.net/gb3Mz/

Related

Get visible from jQuery collection of objects

I have this line of code:
var filterInputs = $(this).siblings('.filterInputs');
which performs some work on filterInputs. Later on, I would like to reduce my collection of filterInputs to just those which are visible.
Clearly, I could do this:
var visibleFilterInputs = $(this).siblings('.filterInputs:visible');
but that seems inefficient given the fact that I already have a reference to the collection I was hoping to reduce.
Is there a way to say something like:
//TODO: Example
var visibleFilterInputs = $(filterInputs:visible);
without having to iterate over the DOM tree again? Thanks
You're absolutely right, there's no reason to recollect the DOM elements, since you already have them in a jQuery object. So that's exactly what the .filter() method is for: http://api.jquery.com/filter/
Try this:
var visibleFilterInputs = filterInputs.filter(":visible");
Here's an example: http://jsfiddle.net/FC9sH/
Note that it's better to target a certain HTML tag, such as <div>, to make the :visible selector a little more efficient (since it isn't part of the CSS specs and can't be optimized by native methods). At least in your case, you're already using the filterInputs class. Anyways, maybe something like:
var visibleFilterInputs = filterInputs.filter("div:visible");
but only if that's applicable. I mean, even selecting multiple known element tags is probably better:
var visibleFilterInputs = filterInputs.filter("div:visible, p:visible");

DRY attribute selection

Say I have an element on the page:
<div data-name='foo'>
What's the best way to get the attribute value? Is there anything more DRY than $('[data-name]').attr('data-name') or $('[data-name]').data('name')?
I don't know how do you define "DRY"...
if (x < 0) x = 1 isn't "DRY" because you wrote x twice?
Anyway with the current DOM structure, no there is not. The selector is inefficient which is more important!
You can give the element an id, which make the code faster.
<div id='foo' data-name='foo'> </div>
...
$('#foo').data('name'); // That's more like it.
I wouldn't know what this has to do with 'Don't Repeat Yourself', but if you don't have a cached reference to this node you would have to use that, rather verbose, call.
It might be a good idea to have at least an id for the nodes. The DOM query for that is lighting fast in comparison to that attribute-selector. But afterall, it's still the fastest technique to store a reference in a variable, like
var myNode = $('[data-name]');
and then use that variable throughout your whole application
myNode.data( 'name' );
You could have a plugin that did something like this. I'm not sure the additional overhead is worth it, though.
function getData(varName) {
var selector = '[data-' + varName + ']';
return $(selector).data(varName);
}
(not tested)
Without a tag qualifier (e.g. div[data-name]) your selector will be very inefficient as it will have to traverse the entire DOM to check for matching elements.
It would be much preferable to identify a specific ID (or even a class) to help out the selector engine and restrict how much of the DOM has to be searched.
As long as you don't repeat that everywhere, (i.e cache the result), I don't really see how you can get anything more DRY.
var name = $('[data-name]').data('name');
You're technically not repeating yourself yet.

How should I define a recurring HTML element & find them all with JavaScript?

I've been trying to work out the best way to define a certain element, which is present an arbitrary number of times throughout a page, without requiring:
the 'name' attribute; or
definition of a new element (XHTML).
I am essentially after the 'name' attribute; however, it appears redundant, obsolete and depreciated in parts.
I am hoping to isolate and manipulate said elements using JavaScript, and preferably avoiding jQuery. Is there a reasonable solution? So far I've thought of:
iterating through all elements with a certain tag and checking for a specific className; or
using incremental IDs on the elements (e.g. el1, el2, el3) and iterating through the sequence until getElementById returns null (feels botchy and only sort of what I'm after).
Thanks :)
querySelectorAll returns a list of the elements within the document (using depth-first pre-order traversal of the document's nodes) that match the specified group of selectors. The object returned is a NodeList.
Reference: https://developer.mozilla.org/en/DOM/Document.querySelectorAll
You can use querySelectorAll, and this is an example you can run in javascript console on this page.
var myList = document.querySelectorAll("a");
for (var c= 0 ; c < myList.length; c += 1) {
console.log(myList[c]);
myList[c].onmouseover= function () {alert(this)}
}
If your reference to a className is deliberate, you could use document.getElementsByClassName(), though this isn't supported by IE 8 and earlier. You could fall back on one of your other techniques for those versions. FWIW, I believe this is how jQuery does it.
You can use className - document.getElementsByClassName()
In HTML 5 you can use data attribute - document.querySelectorAll('[...]');

Can JQuery and Javascript be mixed together?

I am wondering if I could use query and javascript together so I could select an element by class with the javascript and then use javascript to work on that element. Sorry if that didn't make sense. Here is an example:
$('.nav_flag').src = "images/flags/"+userCountryLower+".gif";
Would that work, if not how do I get an element by class using regular javascript. Thanks!
EDIT:I know JQUERY is JavaScript but I was wondering if I could mix jquery selectors and javascript 'controller'-for a loss of a better word
To answer your question as asked, there are several ways to take a jQuery object, i.e., what is returned by $('some selector'), and get a reference to the underlying DOM element(s).
You can access the individual DOM elements like array elements:
// update the src of the first matching element:
$(".nav_flag")[0].src = "images/flags/"+userCountryLower+".gif";
// if you're going to access more than one you should cache the jQuery object in
// a variable, not keep selecting the same thing via the $() function:
var navFlgEls = $(".nav_flag");
for (var i = 0; i < navFlgEls.length; i++) { ... }
But you wouldn't manually loop through the elements when you can use jQuery's .each() method, noting that within the callback function you provide this will be set to the current DOM element:
$(".nav_flag").each(function() {
this.src = "images/flags/"+userCountryLower+".gif";
});
However, jQuery provides a way to set attributes with one line of code:
$(".nav_flag").attr("src", "images/flags/"+userCountryLower+".gif");
To answer the second part of your question, doing the same thing without jQuery, you can use .getElementsByClassname() or .querySelectorAll() if you don't care about supporting older browsers.
jQuery IS Javascript. You can mix and match them together. But you better know what you're doing.
In this case, you probably want to use .attr function to set value of attribute.
Use .attr() in jQuery, rather than mix the two here.
$('.nav_flag').attr('src', "images/flags/"+userCountryLower+".gif");
In many instances, it is fine to mix jQuery with plain JavaScript, but if you have already included the jQuery library, you might as well make use of it. Unless, that is, you have an operation which in jQuery would be more computationally expensive than the same operation in plain JavaScript.
You can do it with jQuery too:
$('.nav_flag').attr("src", "images/flags/"+userCountryLower+".gif");
keep in mind that jQuery is simply a library built upon javascript.
for any jQuery object, selecting its elements by subscription will return the corresponding dom element.
e.g.
$('#foo')[0] // is equivalent to document.getElementById('foo');
You need to add an index to the jQuery object to get the native Javascript object. Change:
$('.nav_flag').src = "images/flags/"+userCountryLower+".gif";
To:
$('.nav_flag')[0].src = "images/flags/"+userCountryLower+".gif";
To get elements by class name in Javascript you can use:
document.getElementsByClassName( 'nav_flag' )[0].src = "images/flags/"+userCountryLower+".gif";
To answer your question, you could use .toArray() to convert the jQuery object into an array of standard DOM elements. Then either get the first element or loop through the array to set all the elements with the class.
However, you could do this easier with pure jquery with attr or prop depending on the version:
$('.nav_flag').attr("src", "images/flags/"+userCountryLower+".gif");
Or use pure javascript:
if (navFlagElements = document.getElementsByClassName("nav_flag") && navFlagElements.length > 0) {
navFlagElements[0].src = "images/flags/"+userCountryLower+".gif"
}

getElementById() wildcard

I got a div, and there i got some childnodes in undefined levels.
Now I have to change the ID of every element into one div. How to realize?
I thought, because they have upgoing IDs, so if the parent is id='path_test_maindiv' then the next downer would be 'path_test_maindiv_child', and therefore I thought, I'd solve that by wildcards, for example:
document.getElementById('path_test_*')
Is this possible? Or are there any other ways?
Not in native JavaScript. You have various options:
1) Put a class and use getElementsByClassName but it doesn't work in every browser.
2) Make your own function. Something like:
function getElementsStartsWithId( id ) {
var children = document.body.getElementsByTagName('*');
var elements = [], child;
for (var i = 0, length = children.length; i < length; i++) {
child = children[i];
if (child.id.substr(0, id.length) == id)
elements.push(child);
}
return elements;
}
3) Use a library or a CSS selector. Like jQuery ;)
In one of the comments you say:
(...) IE is anyway banned on my page, because he doesn't get it with CSS. It's an admin tool for developer, so only a few people, and they will anyway use FF
I think you should follow a different approach from the beginning, but for what it's worth, in the newer browsers (ok, FF3.5), you can use document.querySelectorAll() with which you can get similar results like jQuery:
var elements = document.querySelectorAll('[id^=foo]');
// selects elements which IDs start with foo
Update: querySelectorAll() is only not supported in IE < 8 and FF 3.0.
jQuery allows you to find elements where a particular attribute starts with a specific value
In jQuery you would use
$('[id^="path_test_"]')
Try this in 2019 as a wildcard.
document.querySelectorAll("[id*=path_test_]")
I don't think so wildcards are allowed in getelementById.
Instead you can have all the child nodes of your respective DIV using:
var childNodeArray = document.getElementById('DIVID').childNodes;
This'll give you an array of all elements inside your DIV. Now using for loop you can traverse through all the child elements and simultaneously you can check the type of element or ID or NAME, if matched then change it as you want.
You should not change ID of element to ID of other existing element. It's very wrong, so you better re-think your core logic before going on.
What are you trying to do exactly?
Anyway, to get all elements with ID starting with something, use jQuery as suggested before, if you can't it's also possible using pure JavaScript, let us know.

Categories

Resources