JQuery - Find element that has a given number of siblings - javascript

I need to be able to search through each level of descendants of a JQuery object, and return any collections of siblings that may have a given number of members at that hierarchy level, starting with the objects most distant descendants, and working its way back up.
So given some HTML:
<div class="search-me">
<div> <!-- DON'T FIND THIS -->
<p>FIND ME</p>
<p>FIND ME</p>
<p>FIND ME</p>
</div>
<div> <!-- DON'T FIND THIS -->
<span></span>
<span></span>
</div>
<div> <!-- DON'T FIND THIS -->
<div>FIND ME</div>
<div>FIND ME</div>
<div>FIND ME</div>
</div>
</div>
I need the following pseudo code, to return the items labelled 'FIND ME', and nothing else...
$('.search-me').findGroupWithSize(3);
Also, note that there are three div's containing the <p>, <span>, and <div> tags, which should not be returned. So it should return a collection of the lowest level set of elements, whose siblings total a given amount.

The below example finds the groups of 3 from the HTML you posted.
var siblingsGroupTarget = 3;
var foundIndex = 0;
var elementArray = [];
$(".search-me").find("*").each(function(index){
// Conditions
var hasChilds = $(this).children().length;
var hasSiblings = $(this).siblings().length;
var itsSiblings = $(this).siblings();
var itsSiblingsHasChilds = itsSiblings.children().length;
console.log( $(this)[0].tagName+": "+$(this).siblings().length );
if( hasChilds == 0 && ( hasSiblings == siblingsGroupTarget-1 && itsSiblingsHasChilds == 0 ) ){
elementArray.push( $(this) );
}
});
elementArray.reverse();
for(i=0;i<elementArray.length;i++){
foundIndex++;
elementArray[i].addClass("FOUND").append(" I was found #"+foundIndex);
};
.FOUND{
border:3px solid red;
width:12em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="search-me">
<div> <!-- DON'T FIND THIS -->
<p>FIND ME</p>
<p>FIND ME</p>
<p>FIND ME</p>
</div>
<div> <!-- DON'T FIND THIS -->
<span></span>
<span></span>
</div>
<div> <!-- DON'T FIND THIS -->
<div>FIND ME</div>
<div>FIND ME</div>
<div>FIND ME
<ul> <!-- DON'T FIND THIS -->
<li>FIND ME</li>
<li>FIND ME</li>
<li>FIND ME</li>
</ul>
</div>
</div>
</div>

You can use selector ".search-me > *" passed to jQuery() to get direct descendants of .search-me element, .toArray() to convert jQuery object to array, Array.prototype.reverse() to reverse the collection of elements, .filter() to check if the child elements .length is N, wrap array methods in jQuery() with selector "> *" to return child elements of matched elements
const N = 3;
let res = $("> *", $(".search-me > *").toArray().reverse()
.filter(function(el) {return $("> *", el).length === N}));
console.log(res);
res.css("color", "purple")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div class="search-me">
<div> <!-- DON'T FIND THIS -->
<p>FIND ME</p>
<p>FIND ME</p>
<p>FIND ME</p>
</div>
<div> <!-- DON'T FIND THIS -->
<span>DON'T FIND ME</span>
<span>DON'T FIND ME</span>
</div>
<div> <!-- DON'T FIND THIS -->
<div>FIND ME</div>
<div>FIND ME</div>
<div>FIND ME</div>
</div>
</div>

Related

How to find a parent with a known class in plain JavaScript

Please look at this html code first:
<div class="parent">
<div class="child">
</div>
<div class="child">
<!-- Some code here -->
</div>
</div>
<div class="parent>
<!-- some code here -->
</div>
Suppose, I'm in "child" class, & I need to get the closest parent class of a "child". Remember, there are more than one "parent" class, I just need the closest "parent" only using plain JavaScript, without using JQuery.
How to do that?
You can try using closest()
document.querySelectorAll('.child').forEach(function(child){
console.log(child.closest('.parent'));
});
<div class="parent">
<div class="child">
</div>
<div class="child">
<!-- Some code here -->
</div>
</div>
<div class="parent">
<!-- some code here -->
</div>
You can create a helper function and resuse it.Hope this is what you are looking for -
let getParentClass = node => {
return node.parentNode.classList.value;
}
let child = document.querySelectorAll('.child')[0]; // find the node
console.log(getParentClass(child));
<div class="parent">
<div class="child">
</div>
<div class="child">
<!-- Some code here -->
</div>
</div>
<div class="parent">
<!-- some code here -->
</div>
You can use parentNode to get the closest parent. Or you can use closest from vanilla js.
const child = document.querySelector('.child');
const closestParent = child.parentNode;
const child = document.querySelector('.child');
const closestParent = child.parentNode;
console.log(closestParent);
// Or you can use closest
const closest = child.closest('.parent');
console.log(closest);
<div class="parent">
<div class="child">
</div>
<div class="child">
<!-- Some code here -->
</div>
</div>
<!DOCTYPE html>
<div class="parent">
<div class="child">
</div>
<div class="child">
<!-- Some code here -->
</div>
</div>
<div class="parent">
<!-- some code here -->
</div>
<script>
const child = document.getElementsByClassName('child')[0];
console.log(child)
const parent = child.closest('.parent');
console.log(parent)
</script>
Use this.
var parent = document.querySelectorAll(".child");
for(var p = 0; p < parent.length; p++) {
parent[p].parentElement.classList.add("newClass");
//parent[p].//do something
}
or directly quote the parent
var parent = document.querySelectorAll(".child");
for(var p = 0; p < parent.length; p++) {
parent[p].closest(".parent").classList.add("newClass");
//parent[p].closest(".parent").//do something
}

select content of div with DOM, but without child

there is HTML code:
<div id="parent">
<p>111111</p>
<div id="child">
<p>22222</p>
<div id="childer">
<p>33333</p>
</div>
</div>
</div>
i want only select "22222" in paragraph of div with id=child.
but when use document.getElementById("child").textContent it return "22222" and "33333".
i dont want use jQuery, can anyone help me?
thanks
You can use querySelector(), more info here.
The Document method querySelector() returns the first Element within the document that matches the specified selector, or group of selectors. If no matches are found, null is returned.
var value = document.querySelector('#child p').textContent;
console.log(value);
<div id="parent">
<p>111111</p>
<div id="child">
<p>22222</p>
<div id="childer">
<p>33333</p>
</div>
</div>
</div>
You can try using querySelector() which allows CSS like selector (#child p):
var elText = document.querySelector("#child p").textContent;
console.log(elText);
<div id="parent">
<p>111111</p>
<div id="child">
<p>22222</p>
<div id="childer">
<p>33333</p>
</div>
</div>
</div>
const setup = () => {
const child = document.querySelector('#child');
const p = child.querySelector('p');
console.log(p.textContent);
};
//load
window.addEventListener('load', setup);
<html>
<body>
<div id="parent">
<p>111111</p>
<div id="child">
<p>22222</p>
<div id="childer">
<p>33333</p>
</div>
</div>
</div>
</body>
</html>
document.getElementById("child").getElementsByTagName('p')[0].innerText;

Using class tree to delete specific HTML elements

How can I use vanilla JS to find and delete elements with a specific class X where the parent has class Y?
Example. Given
<div class="likes noise1">
<div class="count noise2">
42
</div>
</div>
<div class="retweets noise3">
<div class="count noise4">
7
</div>
</div>
<div class="messages noise5">
<div class="count noise6">
2
</div>
</div>
I would like to delete the first two ".count" elements (the childs of ".likes" and ".retweets"). The messages div however should be left untouched.
I have tried using querySelectorAll which return a frozen NodeList and iterating it, without success.
You can loop through all the elements to check the Element.className property of the Node.parentNode to remove the element like the following way:
document.querySelectorAll('.count').forEach(function(el){
var classN = el.parentNode.className
if(classN.includes('likes') || classN.includes('retweets'))
el.remove();
});
<div class="likes">
<div class="count">
42
</div>
</div>
<div class="retweets">
<div class="count">
7
</div>
</div>
<div class="messages">
<div class="count">
2
</div>
</div>
OR: You can simply simply specify both the classes as part of the selector, in which case you do not need to check the parentNode as the selector will give you only the elements inside the parents:
document.querySelectorAll('.likes > .count, .retweets > .count').forEach(function(el){
el.parentNode.remove();
});
<div class="likes">
<div class="count">
42
</div>
</div>
<div class="retweets">
<div class="count">
7
</div>
</div>
<div class="messages">
<div class="count">
2
</div>
</div>
Another alternative, further to those already given is to keep an array of the css selector you'll need to find your targets. From there, it's just a simple matter of using querySelector so that the result is still live, albeit in a loop.
"use strict";
function byId(id){return document.getElementById(id)}
window.addEventListener('load', onWindowLoaded, false);
function onWindowLoaded(evt)
{
var tgtSelectors = [ '.likes > .count', '.retweets > .count' ];
tgtSelectors.forEach(removeBySelector);
}
function removeBySelector(curSelector)
{
var tgt = document.querySelector(curSelector);
while (tgt != undefined)
{
tgt.remove();
tgt = document.querySelector(curSelector);
}
}
<div class="likes">
<div class="count">42</div>
</div>
<div class="retweets">
<div class="count">7</div>
</div>
<div class="messages">
<div class="count">2</div>
</div>

How to search for value in all descendant textNodes(Javascript)?

So basically I am making a Chrome extension which sorts the search results on Ebay by their popularity(number of times sold). To do this I need to find all li elements which have a descendant text node containing the text ...+ Sold where ... is a number.
Basically, search results on Ebay look like this:
<li class="s-item">
<div class="s-item__wrapper clearfix">
<div class="s-item__image-section">
<!-- other stuff -->
</div>
<div class="s-item__info clearfix">
<!-- other stuff -->
<div class="s-item__details clearfix">
<!-- other stuff -->
<div><span><span>62+ Sold</span></span></div>
</div>
</div>
</div>
</li>
In every li element I have to search for the text Sold and extract the number out of that text node to process it further. How can I do that?
You cannot do that only by using childNodes or children properties because they return only the children and not all descendants of the current node. So you will have to write your own function for that, something like:
function getDescendants(node, arr) {
var i;
arr = arr || [];
for (i = 0; i < node.childNodes.length; i++) {
arr.push(node.childNodes[i])
getDescendants(node.childNodes[i], arr);
}
return arr;
}
Using this function, you just simply iterate over all descendants and check if they are text nodes ( nodeType == 3 ) and after that search for the word Sold in them. Extracting the number is pretty easy after that.
Something like:
var searchValue = "Sold";
var descendants = getDescendants(listItem);
for(var j = 0; j < descendants.length; j++) {
if(descendants[j].nodeType == 3){
if(descendants[j].nodeValue.indexOf(searchValue) > -1){
var text = descendants[j].nodeValue.trim();
//"37+ Sold" for example
var soldNr = text.substring(0, text.indexOf(searchValue)-2);
//you process your number(soldNr) further
}
}
}
Use a selector string: select li.s-item span to select all spans which descend from an li with a class of s-item, check to see if the span's only child is a text node with "Sold" in it, and if so, do whatever you need to do with it.
If you're sure that any <li> will do, and not just those with a class of s-item, then use 'li span' instead:
document.querySelectorAll('li span').forEach(({ childNodes, textContent }) => {
if (childNodes.length !== 1 || childNodes[0].nodeType !== 3 || !textContent.includes('Sold')) return;
const count = textContent.match(/\d+/);
console.log('Processing span with sold number ' + count);
});
<ul>
<li class="s-item">
<div class="s-item__wrapper clearfix">
<div class="s-item__image-section">
<!-- other stuff -->
</div>
<div class="s-item__info clearfix">
<!-- other stuff -->
<div class="s-item__details clearfix">
<!-- other stuff -->
<div><span><span>62+ Sold</span></span>
</div>
</div>
</div>
</div>
</li>
<li class="s-item">
<div class="s-item__wrapper clearfix">
<div class="s-item__image-section">
<!-- other stuff -->
</div>
<div class="s-item__info clearfix">
<!-- other stuff -->
<div class="s-item__details clearfix">
<!-- other stuff -->
<div><span><span>333+ Sold</span></span>
</div>
</div>
</div>
</div>
</li>
</ul>

Toggle only this item

i've a problem to toggle only (this) 1 'card'
$('a[rel="toggle_comments"]').click(function(){
var div = $('.comments');
$(div, this).slideToggle('slow');
});
<!-- This is a example code -->
<section>
<div class="mainclass">
[...]
<div class="select">
Test
</div>
</div>
<ul class="comments">
<!-- Content -->
</ul>
</section>
<section>
<div class="mainclass">
[...]
<div class="select">
Test
</div>
</div>
<ul class="comments">
<!-- Content -->
</ul>
</section>
[...]
When I clicked at 'toggle_comments', toggle all '.comments' classes, not only that, I have clicked. Anyone have any ideas?
Sorry for my bad english.
I hope you understand what I mean - Thanks :-)
If you want to toggle only the .comments under the <section> tag your link is in, do this:
$('a[rel="toggle_comments"]').click(function(){
// find the closest section tag (parent) and find all child `.comments`
var $comments = $(this).closest('section').find('.comments');
$comments.slideToggle('slow');
});
If you want to toggle all .comments:
$('a[rel="toggle_comments"]').click(function(){
$('.comments').slideToggle('slow');
});

Categories

Resources