querySelector of item by a pattern of id - javascript

I'm trying to get all elements with id feed_item_{n} where {n} can be any integer greater than 0.
I know that I can use document.querySelectorAll('[id^="feed_item_"]') but that doesn't really help because I get also elements with these ids: feed_item_0_x, feed_item_x_0 where x may be any string
Is there a quick way to achieve that in one single line rather than running over all the elements I got from previous command and filttering them?

Since it's not possible to use RegEx within attribute selectors, the only way is to filter your querySelectorAll result; and there.. you can use a regex to match only numbers after feed_item_
It will be something like this
let items = [...document.querySelectorAll('[id^="feed_item_"]')].filter(
(item) => item.id.match(/\d+$/)
);

You also can use :not attribute, to get items whose id doesn't have _x by :not([id*="_x"]
const items = document.querySelectorAll('[id^="feed_item_"]:not([id*="_x"])')
console.log([...items])
<div id="feed_item_1">feed_item_1</div>
<div id="feed_item_2">feed_item_2</div>
<div id="feed_item_x_1">feed_item_x_1</div>
<div id="feed_item_1_x">feed_item_1_x</div>

Related

Why does my function return undefined, especially the .split()parts?

So in essence I get the value of input, then try to divide into into different tags via the comma with this
var noteTags = document.getElementsByClassName("noteTag").value;
Tag = noteTags.split(",");
But in console, the split(",") is undefined
Edit: Sorry I forgot to mention that the noteTag element is input, does this change how the code works in any way?
There are two issues,
getElementsByClassName returns an array-like collection of elements (a NodeList).
And instead of value it should be innerText.
Try like below
var noteTags = document.getElementsByClassName("noteTag")[0].innerText;
Tag = noteTags.split(",");
You are using the split() method on an array. You are also trying to access the value property, whereby you should probably use innerText.
You can use querySelector then you dont need using a key[0] to select the element.
const noteTags = document.querySelector("#noteTag");
console.log(noteTags)
Tag = noteTags.innerHTML.split(",");
console.log(Tag)
<div id="noteTag">en,jp,fr</div>

Get element by a word it contains

Is there a way to get an element by its content(a word it contains?)
For example, get all the elements with the letter "F," and put it in a array of elements
I highly recommand you to use jQuery for these kind of DOM elements searching.
Then you can use this:
var foos = $("div:contains('foo')" )
will make an array with all divs containing the word 'foo'.
One fairly easy way is to select the elements you're interested in and then use 'filter' to look at the innerText. You can make this case insensitive with toLowerCase
var result = $('div').filter( (i,e) => e.innerText.toLowerCase().indexOf("f")>-1);
console.log("Items with 'F':",result.length);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>Forest</div>
<div>Fortnight</div>
<div>Trees</div>
<div>Africa</div>
The simpler way is using :contains('F') as a selector - but that is always case sensitive (which may be fine for your case).
You can use :contains as a selector. For example, to filter all divs of a special class that also contains your text, you can use $("div.myclass:contains('searched text')")
I think you can "bruteforce" it by iterating all DOM items. e.g.:
let arrayDom = Array.from(document.getElementsByTagName("*"));
arrayDom.forEach(element => {
if (element.innerHTML.contains('F')){
// Do something
}
})

Javascript RegExp unknown repeated matches

It's difficult to describe because I'm not an expert with regular expressions. So I tell you my case.
In HTML want to contribute class attributes into different data-xyz attributes. The problem is to get always all classes per match. For example the following HTML:
<span class="note-123 index-3 green">Hello</span> <span class="index-456 red">World<span>
Until now my regular expression is /<span class="([^\"\s]*)\s*/ and it matches the first class. In this case note-123 and index-456
But if I want to get all classes per element I could use /<span class="([^\"\s]*)\s*([^\"\s]*)\s*([^\"\s]*)\s*/. That works until three classes and the result for the second class return index-456, red and an empty string.
Is there a possibility to always get all classes per match no matter how many classes there are? Similar to a nested loop in Javascript?
I would be pleased to get any help from you guys.
You could get the classes without using a regex making use of querySelectorAll to find the elements that you want and use classList to get the class names.
Then use for example the add or remove methods.
Or use a DOMParser.
Note to close the last span.
let elms = document.querySelectorAll("span");
elms.forEach(e => {
for (let value of e.classList.values()) {
console.log(value);
}
});
<span class="note-123 index-3 green">Hello</span> <span class="index-456 red">World</span>
Use the regex to extract the value of the class attribute and split it at whitespace sequences:
let as_classes
, as_matches
, n_i
, re_classes
, s_test
;
re_classes = new RegExp ( "<span class=\u0022([^\\u0022]*)", "g" );
s_test = '<span class="note-123 index-3 green">Hello</span> <span class="index-456 red">World<span>';
n_i=0;
while ((as_matches = re_classes.exec(s_test)) !== null) {
n_i++;
s_classes = as_matches[1];
as_classes = s_classes.split(/[\s]+/g);
console.log(`match #${n_i}, classes: ${JSON.stringify(as_classes)}.`);
}
Warning
It is in general never a good approach to extract information from html with regexen.

Efficiently test if an Element will be in a jQuery collection

Given a jQuery collection like this $(selector,containerElement), is it possible to test if a given Element will be part of that collection without constructing the collection at all.
That is, without doing this: $(selector,containerElement).is(someElement), which will construct a jQuery object with all the matched elements inside and then check if one of those elements is someElement.
Is there a more efficient way of doing it?
PS: Keep in mid that jQuery supports additional selector syntax like :has(), :lt(), :first and relative selectors like > tagName, + tagName.
I ended up implementing my own custom solution for this.
The following function does the trick.
function jQueryMatch(selector, container, element){
let attrName = '_dummy_876278983232' ;
let attrValue = (''+Math.random()).slice(2) ;
$(container).attr(attrName,attrValue) ;
selector = selector.replace(/(,|^)(\s*[>~+])/g,`$1 [${attrName}=${attrValue}]$2`) ;
return $(element).is(selector) ;
}
jQueryMatch(selector, container, element) is equivalent to $(selector, container).is(element).
I measured jQueryMatch to be between 3 and 10 times faster depending on the complexity of the selector. On bigger DOM trees, the difference will be even greater.
The function is super simple so it's easy to figure out what it does.
$.contains(containerElement, someElement);
If performance is an issue, you could use vanilla JS for this, such as:
[...yourContainer.querySelectorAll('yourSelector')].includes(elementToTest)
Sample:
let container = document.querySelector('[data-container');
let insideElement = container.querySelector('[data-inside-element]');
let outsideElement = container.querySelector('[data-outside-element]');
console.log([...container.querySelectorAll('[data-inside-element]')].includes(insideElement));
console.log([...container.querySelectorAll('[data-inside-element]')].includes(outsideElement));
<div data-container>
<p data-inside-element>element1</p>
<p data-inside-element>element2</p>
<p data-inside-element>element3</p>
</div>
<p data-outside-element>outside element</p>
Expanding on Brian Reynolds's answer:
$.contains(containerElement, someElement) && someElement.is(selector)
However, this won't work if selector starts with operators like >, +, or ~ that require it to be in a specific position relative to containerElement, since we've separated those variables.

Mootools Selector issue

Right now I have a dynamic string that assigns it's values to a particular div class.
Output looks like this
<div class="12923"></div>
I want to find that 'randNumber' div, then check if it has another class 'x'
Currently what I have now doesn't work:
var randNumber = 12923
var lookingForYou = $$('.'+randNumber);
if (lookingForYou.hasClass('XCLASS')){alert('XCLASS FOUND!');}
$$ returns an Elements instance, Elements is an array-like Class
anyway since you are basically filtering, you can tell Slick that you need an element with both class:
var randNumber = 12923;
if($$('.' + randNumber +'.XCLASS').length>0){
alert('XCLASS FOUND');
}else{
//dostuff
}
or you could just use one of the Elements methods, I think .some will be your best choice here:
var randNumber = 12923
var lookingForYou = $$('.' + randNumber);
alert(lookingForYou.some(function(el){
return el.hasClass('XCLASS');
}))
EDIT:
adding some links:
A better way to use Elements on MooTools blog
in my second example I used the some method, which, by looking at the source is not overloaded, but is just the one in Array.prototype.some:
Element.js source reference
Array.some on MDN
$$ returns an array of all matching elems. Not sure if you can do a hasclass on an array. Might have to do a .each() then do it. Try $('body').getElement('.'+randNumber).hasClass('XCLASS') this way you grab 1 elem if you don't want to mess with the array.
Here:
if (lookingForYou.hasClass('XCLASS')){alert('XCLASS FOUND!');}
$$() returns an array, and hasClass() performs the check on each element of the array, returning an array of booleans. Unfortunately, when you check if (...), then the return array, even if all of the values are false, is evaluated as true because it's non-empty.

Categories

Resources