How to query elements by attribute value instead of attribute name - javascript

So this is an interesting one and may very well be impossible to do efficiently. But I'm interested in finding an efficient way to query all html elements in the document that have a particular value set for any attribute. So, for example, instead of this:
document.querySelectorAll('[attrName]');
I'm looking for the equivalent of the following pseudo-code:
document.querySelectorAll('[*=specificValue]');
So essentially the result would be all elements that have any attribute whose value matches "specificValue". I know there are gross ways to do this such as:
var all = document.querySelectorAll('*');
var matches = [];
var attrs;
for (var i = 0; i < all.length; i += 1) {
attrs = Array.prototype.slice.call(all[i].attributes);
for (var j = 0; j < attrs.length; j += 1) {
if (attrs[j].value === 'specificValue') {
matches.push(all[i]);
break;
}
}
}
However, I would really love to avoid analyzing every single html element like this. Any ideas?
Edit:
Thanks for all the help so far. Before too many people give alternate suggestions I should explain what this is for. Basically, it's an experiment. The idea was that I might be able to create live object-to-dom databindings like what you get in Ember.js but instead of having to compile templates, you could just use regular html attributes and have a syntax marker in the value itself that a binding should be created. For example: . I figured this might be fun if there was an efficient way to select relevant elements on the fly. Clearly I know a loop has to happen somewhere. However, if the browser is running a native iteration, I'd prefer that over my own JavaScript loop. I just wasn't sure if there was some "secret" selector syntax I wasn't aware of or if anyone could think of any other cool tricks.

I wouldn't worry too much about performance at this point, but focus on code efficiency. If you don't want to incooporate any jQuery, and just use vanilla JS, then I would just store your attributes values in a class name. For example, if you have a following HTML element:
<div class="something" yo="1"></div>
I'd put entire attributes and values in a class name like
<div class="something yo1"></div>
Then you can simply use already built-in method to select every elements with the class name specified above.
var elems = document.getElementsByClassName("yo1"); //make sure to cache these selected elems in a variable.
console.log(elems);
If you want to use jQuery, things get easier, because you can simply select element by attribute selector and it works across all browsers.
http://api.jquery.com/attribute-equals-selector/

If you don't search for it, you cannot find it. So, basically you need to iterate through all the elements.
However, you may use some simple logic to at least help you with the performance.
Define a context
For example, you may have a really long HTML but within there, you may have a DIV where only inside of that div you may have your elements with the value "specificValue". In this case, you know that what you are looking for only resides within this div and only search within it.
You still have to go through all the elements but this time not within the whole HTML but only within the DIV that you expect the values to be.
So to summarize whether you use jQuery or plain javascript, you still have to go through all the elements. The solution is to narrow down the searchable "area" by defining a context and only searching within that..
E.g.
<html>
<head>
<title>some title</title>
</head>
<body>
.
.
<div class="searchable">
-- your elements that may have the value you will search
</div>
.
.
.
<div class="searchable">
-- your elements that may have the value again.
</div>
</body>
</html>
Mind you - you will have to itereate through all the elements again to find the divs with the class name "searchable". With jQuery $('.searchable') also does this. But you can also define a context for that and say $('.searchable', 'body') to narrow down that search area too..

Related

javascript .value wont get anything [duplicate]

How can I get a collection of elements by specifying their id attribute? I want to get the name of all the tags which have the same id in the html.
I want to use ONLY getElementById() to get an array of elements. How can I do this?
I know this is an old question and that an HTML page with multiple identical IDs is invalid. However, I ran into this issues while needing to scrape and reformat someone else's API's HTML documentation that contained duplicate IDs (invalid HTML).
So for anyone else, here is the code I used to work around the issue using querySelectorAll:
var elms = document.querySelectorAll("[id='duplicateID']");
for(var i = 0; i < elms.length; i++)
elms[i].style.display='none'; // <-- whatever you need to do here.
The HTML spec requires the id attribute to be unique in a page:
[T]he id attribute value must be unique amongst all the IDs in the element's tree
If you have several elements with the same ID, your HTML is not valid.
So, document.getElementById should only ever return one element. You can't make it return multiple elements.
There are a couple of related functions that will return a list of elements: getElementsByName or getElementsByClassName that may be more suited to your requirements.
Why you would want to do this is beyond me, since id is supposed to be unique in a document. However, browsers tend to be quite lax on this, so if you really must use getElementById for this purpose, you can do it like this:
function whywouldyoudothis() {
var n = document.getElementById("non-unique-id");
var a = [];
var i;
while(n) {
a.push(n);
n.id = "a-different-id";
n = document.getElementById("non-unique-id");
}
for(i = 0;i < a.length; ++i) {
a[i].id = "non-unique-id";
}
return a;
}
However, this is silly, and I wouldn't trust this to work on all browsers forever. Although the HTML DOM spec defines id as readwrite, a validating browser will complain if faced with more than one element with the same id.
EDIT: Given a valid document, the same effect could be achieved thus:
function getElementsById(id) {
return [document.getElementById(id)];
}
document.querySelectorAll("#yourId"); returns all elements whose id is yourId
It is illegal to have multiple elements with the same id. The id is used as an individual identifier. For groups of elements, use class, and getElementsByClassName instead.
The id is supposed to be unique, use the attribute "name" and "getelementsbyname" instead, and you'll have your array.
As others have stated, you shouldn't have the same ID more than once in your HTML, however... elements with an ID are attached to the document object and to window on Internet Explorer. Refer to:
Do DOM tree elements with ids become global variables?
If more than one element with the same ID exists in your HTML, this property is attached as an array. I'm sorry, but I don't know where to look if this is the standard behavior or at least you get the same behavior between browsers, which I doubt.
Class is more than enough for refering anything you want, because it can have a naming with one of more words:
<input class="special use">
<input class="normal use">
<input class="no use">
<input class="special treatment">
<input class="normal treatment">
<input class="no special treatment">
<input class="use treatment">
that's the way you can apply different styles with css (and Bootstrap is the best example of it) and of course you may call
document.getElementsByClassName("special");
document.getElementsByClassName("use");
document.getElementsByClassName("treatment");
document.getElementsByClassName("no");
document.getElementsByClassName("normal");
and so on for any grouping you need.
Now, in the very last case you really want to group elements by id. You may use and refer to elements using a numerically similar, but not equal id:
<input id=1>
<input id="+1">
<input id="-1">
<input id="1 ">
<input id=" 1">
<input id="0x1">
<input id="1.">
<input id="1.0">
<input id="01.0">
<input id="001">
That way you can, knowing the numeric id, access and get an element by just adding extra non-invalidating numeric characters and calling a function to get (by parsing and so on) the original index from its legal string identifying value. It is useful for when you:
Have several rows with similar elements and want to handle its events
coherently. No matter if you delete one or almost all of them.
Since numeric reference is still present, you can then reuse them and
reassign its deleted format.
Run out of class, name and tagname identifiers.
Although you can use spaces and other common signs even when it's a not a requirement strictly validated in browsers, it's not recommended to use them, specially if you are going to send that data in other formats like JSON. You may even handle such things with PHP, but this is a bad practice tending to filthy programming practices.
This is my solution:
<script type="text/javascript">
$(document).ready(function () {
document.getElementsByName("mail")[0].value = "ex_mail1";
document.getElementsByName("mail")[1].value = "ex_mail2";
});
</script>
Or you can use for-loop for that.
You shouldn't do that and even if it's possible it's not reliable and prone to cause issues.
Reason being that an ID is unique on the page. i.e. you cannot have more than 1 element on the page with the same ID.
you can use
document.document.querySelectorAll("#divId")
we can use document.forms[0].Controlid
If you're using d3 for handling multiple objects with the same class / id
You can remove a subset of class elements by using d3.selectAll(".classname");
For example the donut graph here on http://medcorp.co.nz utilizes copies of an arc object with class name "arc" and there's a single line of d3, d3.selectAll(".arc").remove(); to remove all those objects;
using document.getElementById("arc").remove(); only removes a single element and would have to be called multiple times (as is with the suggestions above he creates a loop to remove the objects n times)

Override DOM elements with javascript

In a nutshell I'm trying to target an element within the DOM, then inject a class on the fly to later alter that element.
The situation is as follows, I am working with an application that has predetermined mark up (Manage Engine). It's a tool for system work flows, creating a centralized portal for ticket logging, asset management blah blah. So I use the tool to create templates for end users to log service requests. This is accessed via a web interface portal which in turn obviously has mark up.
So far I have been able to alter specific things such as background colors on table headers for example. I achieve this by creating a rule to fire within that template upon load time. So essentially I am allowing the template to load with its predetermined code and then I am applying a for loop to alter the code once it has loaded. (Hacky I know, however its working really well).
The issue I'm running into now is that certain things within the mark up are generic (no class or id associated to the element). My plan is to target that specific generic element as a variable then add my own class to it upon load. Is there a way to target an element that has a class and then target the child elements within, save that child as a variable to then add a class on the fly with javascript. Please see example below.
<tr class=test1>
<td>
<input>
<input>
<input>
</td>
<tr/>
So with the example above what I am trying to achieve is add my own class with JavaScript to the <td> element. Obviously if i target just <td> it will alter all <td> elements within the markup. Can i get to that specific <td> via the <tr> parent with the test1 class. I am currently unable to use any jquery requests as the base code can not be touched.
Again I know this is a little backwards and hacky but it does work with anything I can specifically target (has a class or id). I need to be able to do this with pure JavaScript. Any suggestions or help is greatly appreciated, apologies if this is a noob approach or question, first time posting in a forum. Let me know if further examples or information is required.
document.querySelector("body").children can get all child elements of body
step1: Select the class. The variable t1 will contain an Array of tr elements.
var t1 = document.querySelector('.test1');
step2: Get the first value from the array t1. So, tr_el1 contains one tr element.
var tr_el1 = t1[0];
step3: Get the children of tr. td_el contains an Array of td elements.
var td_el = tr_el1.children;
Now you can use the td from the td_el array
var tr = document.getElementsByClassName('test1')[0]
var td = tr.children[0]
var inputs = Array.prototype.slice.apply(td.children)
Now you got your inputs inside an array. You're welcome ;-)
Thanks a lot for the assistance, really appreciate it. The examples above helped me build a hybrid that although not exact has given me the outcome i needed.
var parent = document.querySelector(".roweven").children;
var nS;
for (nS = 0; nS < parent.length; nS++) {
parent[nS].style.position = "absolute";
parent[nS].style.width = "100%";
}
I am yet to wrap this up in a function but working as intended. Thanks again :) :)

Fastest way to hide thousands of <li> elements?

I have an autocomplete form where the user can type in a term and it hides all <li> elements that do not contain that term.
I originally looped through all <li> with jQuery's each and applied .hide() to the ones that did not contain the term. This was WAY too slow.
I found that a faster way is to loop through all <li> and apply class .hidden to all that need to be hidden, and then at the end of the loop do $('.hidden').hide(). This feels kind of hackish though.
A potentially faster way might be to rewrite the CSS rule for the .hidden class using document.styleSheets. Can anyone think of an even better way?
EDIT: Let me clarify something that I'm not sure too many people know about. If you alter the DOM in each iteration of a loop, and that alteration causes the page to be redrawn, that is going to be MUCH slower than "preparing" all your alterations and applying them all at once when the loop is finished.
Whenever you're dealing with thousands of items, DOM manipulation will be slow. It's usually not a good idea to loop through many DOM elements and manipulate each element based on that element's characteristics, since that involves numerous calls to DOM methods in each iteration. As you've seen, it's really slow.
A much better approach is to keep your data separate from the DOM. Searching through an array of JS strings is several orders of magnitude faster.
This might mean loading your dataset as a JSON object. If that's not an option, you could loop through the <li>s once (on page load), and copy the data into an array.
Now that your dataset isn't dependent on DOM elements being present, you can simply replace the entire contents of the <ul> using .html() each time the user types. (This is much faster than JS DOM manipulation because the browser can optimize the DOM changes when you simply change the innerHTML.)
var dataset = ['term 1', 'term 2', 'something else', ... ];
$('input').keyup(function() {
var i, o = '', q = $(this).val();
for (i = 0; i < dataset.length; i++) {
if (dataset[i].indexOf(q) >= 0) o+='<li>' + dataset[i] + '</li>';
}
$('ul').html(o);
});
As you can see, this is extremely fast.
Note, however, that if you up it to 10,000 items, performance begins to suffer on the first few keystrokes. This is more related to the number of results being inserted into the DOM than the raw number of items being searched. (As you type more, and there are fewer results to display, performance is fine – even though it's still searching through all 10,000 items.)
To avoid this, I'd consider capping the number of results displayed to a reasonable number. (1,000 seems as good as any.) This is autocomplete; no one is really looking through all the results – they'll continue typing until the resultset is manageable for a human.
I know this is question is old BUT i'm not satisfied with any of the answers. Currently i'm working on a Youtube project that uses jQuery Selectable list which has around 120.000 items. These lists can be filtered by text and than show the corresponding items. The only acceptable way to hide all not matching elements was to hide the ul element first than hide the li elements and show the list(ul) element again.
You can select all <li>s directly, then filter them: $("li").filter(function(){...}).hide() (see here)
(sorry, I previously posted wrong)
You can use the jQuery contains() selector to find all items in a list with particular text, and then just hide those, like this:
HTML:
<ul id="myList">
<li>this</li>
<li>that</li>
<ul>​
jQuery
var term = 'this';
$('li:contains("' + term + '")').hide();​
You could use a more unique technique that uses technically no JavaScript to do the actual hiding, by putting a copy of the data in an attribute, and using a CSS attribute selector.
For example, if the term is secret, and you put a copy of the data in a data-term attribute, you can use the following CSS:
li[data-term*="secret"] {
display: none;
}
To do this dynamically you would have to add a style to the head in javascript:
function hideTerm(term) {
css = 'li[data-term*="'+term+'"]{display:none;}'
style = $('<style type="text/css">').text(css)
$('head').append(style);
}
If you were to do this you would want to be sure to clean up the style tags as you stop using them.
This would probably be the fastest, as CSS selection is very quick in modern browsers. It would be hard to benchmark so I can't say for sure though.
How about:
<style>
.hidden{ display: none; }
</style>
That way you don't have to do the extra query using $('.hidden').hide() ?
Instead of redefining the Stylesheets rules, you can directly define 'hide'
class property to "display:none;" before hand and in your page, you can just
apply the class you defined after verifying the condition through javascript,
like below.
$("li").each(function(){if(condition){$(this).addClass('hide');}});
and later, if you want to show those li's again, you can just remove the class like below
$("li").each(function(){if(condition){$(this).removeClass('hide');}});

Accessing Objects inside HTML Object with JS

I am creating an array of div tags inside the player table div. I'm getting all div tags with class .players. The divs with class name .players have input fieds and a link field inside. I want to be able to manipulate these (remove, add class, etc...)
What I thought would work would be something like:
$(divarray[j]+' .link').hide();
$(divarray[j]+' a').remove('.link');
But it's not working. Any thoughts? I'm sure it's something simple but it's my first time at JS :)
var divarray = $('#player-table > .players');
for( var j = 0; j < 10; j++){
$(divarray[j]).removeClass("players");
$(divarray[j]).addClass("selected_players");
$('#debug').append(divarray[j]);
$(divarray[j]+' a').hide();
}
First of all, you cannot just concatenate jQuery objects or DOM nodes with strings to create new selectors. jQuery provides methods for this kind of situations, where you already have an object or DOM node and want to find other related nodes.
Second, with jQuery there are much better ways to process a set of elements. Here is your code in more jQuery-like way. This is just an example, because I don't know the HTML structure. You have to adjust it so that it selects and applies to the correct elements.
$('#player-table > .players').slice(0,10) // gets the first 10 elements
.removeClass("players") // removes the class from all of them
.addClass("selected_players") // adds the class
.find('a').hide().end() // finds all descendant links and hides them
.appendTo('#debug'); // appends all elements to `#debug`
As you maybe see, there is only one semicolon at the last line. That means this whole code block is just one statement, but splitting it up over several lines increases readability.
It works because of the fluent interface, a concept which jQuery heavily makes use of. It lets you avoid creating jQuery objects over and over again, like you do ($(divarray[j])).
Another advantage is that you can work on the whole set of elements at once and don't have to iterate over every element explicitly like you have to do with "normal" DOM manipulation methods.
For learning JavaScript, I recommend the MDN JavaScript Guide.
jQuery has a couple of tutorials and a very good API documentation.
Read them thoroughly to understand the basics. You cannot expect to be able to use a tool without reading its instructions first.
Try this istructions
$(divarray[j]).find('.link').hide();
$(divarray[j]).find('a').remove('.link');
Try also
$(divarray[j]).find('.link:first').hide();
If you need to work only on the first element
Hope it helps

How can I check if an element is within another one in jQuery?

Is there any direct way in JavaScript or jQuery to check if an element is within another one.
I'm not referring to the $(this).parent as the element I wish to find can be a random number steps lower in the tree of elements.
As an example, I would like to check if < div id="THIS DIV"> would be within < div id="THIS PARENT">:
<div id="THIS_PARENT">
<div id="random">
<div id="random">
<div id="random">
<div id="THIS_DIV">
(... close all divs ...)
So in pseudo code:
if($("div#THIS_DIV").isWithin("div#THIS_PARENT")) ...
If there isn't any direct way I'll probably do a function for this but still it's worth asking.
You could do this:
if($('#THIS_DIV','#THIS_PARENT').length == 1) {
}
By specifying a context for the search (the second argument) we are basically saying "look for an element with an ID of #THIS_DIV within an element with ID of #THIS_PARENT". This is the most succint way of doing it using jQuery.
We could also write it like this, using find, if it makes more sense to you:
if($('#THIS_PARENT').find('#THIS_DIV').length == 1) {
}
Or like this, using parents, if you want to search from the child upwards:
if($('#THIS_DIV').parents('#THIS_PARENT').length == 1) {
}
Any of these should work fine. The length bit is necessary to make sure the length of the "search" is > 0. I would of course personally recommend you go with the first one as it's the simplest.
Also, if you are referring to an element by ID it's not necessary (although of course perfectly okay) to preface the selector with the tag name. As far as speed, though, it doesn't really help as jQuery is going to use the native getElementById() internally. Using the tag name is only important when selecting by class, as div.myclass is much, much faster than .myclass if only <div> elements are going to have the particular class.
With jQuery >=1.4 (2010) you can use the very fast function jQuery.contains()
This static method works with DOM elements, not with jQuery elements and returns true or false.
jQuery.contains( container, descendant )
Example: To check if a element is in the document you could do this:
jQuery.contains( document.body, myElement )

Categories

Resources