I wish to write something like:
var N = document.querySelectorAll("input[id^='left"+/^[0-9]*$/+"right']").length;
How do I do in JS / JQuery?
You can't. Attribute selectors do not support regexp. You can select based on prefix and suffix, though:
[id^="left"][id$="right"]
If you want to exclude elements with IDs missing the digits in between left and right, you'll have to filter them out:
Array.from(queryResults).filter(elt => /^left\d*right$/.test(elt.id));
In general, however, using IDs with complex internal structure is often not the best approach. You will have to keep constructing them and picking them apart. Consider using classes and/or data attributes, or it's often the case that if you are creating the elements yourself, you can just remember the elements and reference them directly.
Related
I have a html code like
<div>
<span>TV</span>
</div>
I want to find this span through documentObject having text 'TV', like getElementById etc ... something like getElementByText. I know that it's possible through XPath/JQuery/Regex.
But I need it to get through DOM object model only. As only DOM model is available in my context.
I see couple of answers:
Finding an html element ID based on a text displayed
jquery - find element that only has text and not any other html tag
how to find element after some text with javascript?
But these are not helpful to me, as I need to get it through DOM model only.
Assuming the document is well-formed enough to parse into a proper DOM object tree, you can iterate through the entire structure without using an external library. Depending on the structure, you may have to examine every node to find all matches, and this may be slow. If you have access to IDs of any sort, you may be able to reduce search scope and improve performance.
The key property you will need is the childNodes collection on every DOM node. Starting with the BODY (or some other container), you can recurse through all the child nodes.
This site is pretty basic but shows dependency-free methods for accessing DOM elements. See the section called "Tools to Navigate to a Certain Element".
I noticed that you mentioned regular expressions as a means to find elements. Regexes are poor for parsing entire documents, but they can be very useful in evaluating the textual content of a single node (e.g. partial matches, pattern matches, case insensitivity, etc.) Regular expressions are part of the JavaScript language itself and have been so for well over a decade.
Only thing I can think of is something like this:
function getElementByTextContent(text)
{
var spanList = document.getElementsByTagName("span");
for (var i = 0, len = spanList.length; i < len; i++)
{
if(spanList[i].textContent === text) // use .innerHTML if you need IE compatibility
return spanList[i]
}
}
of course it assumes you are only searching for <span> elements, but this might work for you. Here's a demo as well:
http://jsfiddle.net/uATdG/
Actually, I want to get multiple pseudo-element to using querySelectorAll.
I read some of the related articles there all of the developers say we haven't access to use querySelectorAll to get multiple pseudo-element.
We can get just one element to use querySelector and code like the bellow the code.
const textAfter = window.getComputedStyle(
document.querySelector(".text-after"), ":after"
).getPropertyValue("color")
console.log(textAfter)
But sometimes our project purpose needs to use multiple selectors.
So, that's why have anyone experienced in here? who can help to solved this problem?
Thanks to #All
Reverse the order. Instead of trying to compute the properties of a list of elements, for each element in the list, compute the properties for each single element:
const elements = Array.from(document.querySelectorAll(`.text-after`));
const allOfThem = elements.map(element =>
window.getComputedStyle(element, ":after").getPropertyValue("color")
);
console.log(allOfThem);
(We use Array.from here, because while an HTMLCollection comes with .forEach, it does not come with .map so we need to force it into being a real array before we do our mapping)
Yes, there are similar questions, but they are about jquery adding lowercase attributes like here: Does the attr() in jQuery force lowercase?
But I have a different situation. Here is my HTML5 piece of code:
<tr class='projectRow' data-projectId='34'>
Notice that this is camelCase. And now this code does not work:
//"this" points to a child element
$id = $(this).closest('.projectRow').data('projectId');//undefined
But if I make it lowercase:
$(this).closest('.projectRow').data('projectid');
It works.
When I look at the source code, it's clearly "projectId" (camelCase), but when in chrome -> dev tools -> elements then it's "projectid" (lowercase) o_O
No wonder jquery can't get this value, but why is Chrome doing this? I did something similar hundreds of times before, although was using a - like in "project-id" and now after so many years of making web applications I discover something like this o_O
EDIT
The HTML spec states attribute names are case-insensitive, meaning writing them all as uppercase is as good as writing them all in lowercase or in camelCase:
Attribute names for HTML elements may be written with any mix of lowercase and uppercase letters that are a case-insensitive match for
the names of the attributes given in the HTML elements section of this
document; that is, attribute names are case-insensitive.
EDIT #2
Another part of the spec states it more explicitly:
All attribute names on HTML elements in HTML documents get
ASCII-lowercased automatically, so the restriction on ASCII uppercase
letters doesn't affect such documents.
Original Answer
jQuery specifies that if you want to access attributes via camelCase, then hyphenate them such that:
data-project-id="1" is accessed via $(element).data('projectId');
Instead of using ".data()" you could just use ".attr()" to access the value of the attribute, then you could reference it by the name you've given it; like the following:
$(element).attr('data-projectId');
If you're ok with ordinary Javascript and use HTML5 this should work:
element.getAttributeNS(null, 'data-projectId')
As part of a Chrome extension I am searching the entire DOM for elements containing specific words inside each ID/Class.
Currently it looks something like:
"allSelectors": document.querySelectorAll("[id*='example'][class*='example']"),
"collapse": function () {
for (var i = 0; i < MyObject.allSelectors.length; i++) {
// Manipulate MyObject.allSelectors[i] etc
}
},
First, I would like to restructure it somehow (possibly using an array?) so that it is easy to add new selectors as doing it like this:
document.querySelectorAll("[id*='example'][class*='example'][id*='other'][class*='other']")
Isn't easily scaleable or good.
Secondly, I think document.querySelectorAll is very slow - the reason I am using it is because I need to search anywhere in an id/class (hence the use of *=) and cannot use a big external library (such as jQuery), as this is a small file and is being injected user side.
Is there an any solution to either of these problems? because if there are many matches then this slowness might become an issue.
First of all I would totally go for querySelectorAll, I don't think it's that slow, and also it totally fits in a situation like yours. I agree with you that adding a library is overkill for this, and additionally it might not be as beneficial as someone thinks (let's test it here).
Then, again I agree with you that the current solution is not very scalable and that the array is the way to go. Here's a very basic implementation using an array:
// an array of classes and ids to match
var nodes,
searches = [
'[id*="test"]',
'[class*="example"]'
];
// a simple function to return an array of nodes
// that match the content of the array
function getNodes(arr){
return Array.prototype.slice.call(document.querySelectorAll( arr.join() ));
}
nodes = getNodes(searches);
The good thing is that new classes and ids can be easily added or removed from the array, for example, later on you can add:
searches.push('[id*="some"]');
nodes = getNodes(searches); // new nodes will be fetched
Here's a jsbin with a full example code.
I am writing a jQuery plugin and I am at the optimization stage.
I wonder which of these leads to a quicker script in general, and what sort of mitigating factors are important:
Have the script generate lots of classes and ids at the start so finding elements in the dom is easier (quicker too?) later on.
Keep classes and ids to a minimum (so save time by not creating them), but then look for elements in more convoluted ways later (eg the nth item in an object), which I presume is slower than getting by id/class.
The way the browser works is that upon load it creates an in-memory DOM tree which looks as follows:
P
_______________|______________
| |
childNodes attributes
______________|___________ |
| | | title = 'Test paragraph'
'Sample of text ' DIV 'in your document'
|
childNodes
__________|_______
| | |
'HTML you might' B 'have'
So when you lookup P > DIV > B, the lookup has to find all P elements, then find all DIV elements within P and then find all B elements within DIV. The deeper the nesting the more lookups it needs to do. Further, it might find all P > DIV only to find that none of them have B and it will have wasted time looking through all P > DIV matches.
Lookups by ID are faster because IDs are guaranteed to be unique so the DOM can store them as hash table entries which have very fast lookups. In terms of jQuery the implementation might be slightly different, however, document.getElementById has the fastest lookup time so $('#my_node_id') should also be quite fast.
Consequently, if your node doesn't have an ID, you can find the nearest ancestor that does and find your node relative to that ancestor
#my_node_id > div > b
because the look up only needs to happen under the sub-tree of #my_node_id it will be faster than p > div > b
The question is not really specific enough so I can give you advice directly relevant to your code, but here are some of my favorite jQuery optimization tips:
Whenever possible, specify a context! Don't make jQuery look places it doesn't have to. If you know that something is going to be inside the #container div, then do $(something, '#container');
.myclass is slow. Internally, jQuery has to go through every single element to see if it has the class you are searching for, at least for those browsers not supporting getElementsByClassName. If you know that a class will only be applied to a certain element, it is way faster to do tag.myclass, as jQuery can then use the native getElementsByTagName and only search through those.
Don't do complicated selectors in one bang. Sure, jQuery can figure out what you want, but it takes time to parse out the string and apply the logic you want to it. As such, I always like to separate my "queries" out into patches. While most people might do $('#myform input:eq(2)'); or something like that, I prefer to do $('input','#myform').eq(2); to help jQuery out.
Don't re-query the DOM if you plan on doing multiple things with a set of objects. Take advantange of chaining, and if not possible to use chaining for whatever reason, cache the results of the query into a variable and perform your calls on that.
I think you answered your own question: 'convoluted ways' is a synonym for 'it will break' - any changes to the html structure will break your code.
For example, imagine you're trying to get myDiv and you assume it's the last sibling of child:
<div>parent
<div>child</div>
<div>myDiv</div>
</div>
What happens if you later decide the structure should really be like this?
<div>parent
<div>child</div>
<div>myDiv</div>
<div>child</div>
</div>
By relying on assumptions about structure your code becomes really brittle. Adding classes and ids to nodes will prevent such scenarios.
I think you should go with the first choice. Do also remember that getting nodes by class is always slower than getting them by id.
With my plugins I try, as best I can, to limit the amount of hooks I introduce into the document. By "hooks" I mean IDs or classes.
The best way to avoid them completely is to retain references to any created elements within your plugin's closure. For example:
jQuery.fn.addList = function(items, fn){
var $list = $('<ul/>').html('<li>' + items.join('</li><li>') + '</li>');
return this.each(function(){
$(this).append($list);
fn.call($list);
});
};
$('body').addList(['item 1', 'item 2'], function(){
var referenceToTheList = this;
console.log(referenceToTheList, '<- this is my list, no hooks involved!');
});
The list can be carried around JavaScript functions (referenced and used in multiple places) without requiring any hooks in the HTML; thus making it as unobtrusive as possible!
Avoiding/limiting hooks is especially important in plugin development because you never know where it might end up being used.