Select only child divs, and not "grandchild" divs - javascript

I have a function that recursively adds more divs to a HTML page upon user interaction. These divs represent objects and the inputs represent these object's properties. The problem is that these objects are nested inside eachother - the part where I need help in is by selecting only child div's informations. I'll try and illustrate the issue:
<div class="all">
<div class="resource"> //lets call this resource "resource 1"
<div class="method"> // this method as "method 1"
<div class="method"> // this method as "method 2"
<div class="resource"> // this resource as "resource 2"
<div class="method"> // this method as "method 3"
<div class="resource">
<div class="resource">
...
We have this div "resource", that inside it can be found either another "resource" or "method". Now, the problem is I have to select only child divs in loops. Example:
I'd have to select only the methods inside resource 1.
What i've tried:
Selecting it using css selectors:
resource1.querySelectorAll(".resource > .method")
but this returns me all three methods (and I need it to return only the 2 method nested directly beneath it). I believe this happens because this selector searches for all divs "method" beneath "resouce" divs (and as all these nested divs have the same classnames, it cannot tell them apart).
Selecting it using html selection. e.g:
resource1.getElementsByClassName("method");
again, I needed it to return me only the methods directly beneath document.getElementsByClassName("resource")[0] (that is equal to resource 1), but instead, it returns me not only the methods directly beneath it, but also all the methods found in resources inside resource 1.
Using Jquery:
Searching for more possible solutions, I've found this Jquery line:
$('.resource').find('.method').first().siblings('.method').addBack().show()
but I believe it does not work for my case. For this I made it so all primary resources ("resource" divs that are children to no other "resource") have className = "primaryResource". so that:
$('.primaryResource') //returns me all the primary resource divs. This works as intended.
$('.primaryResource')[0] //then returns me the first primary div, but
$('.primaryResource')[0].find('.method') or $('.primaryResource')[0].find('.resource') // does
not return me anyhting, instead it catches Uncaught TypeError: $(...)[0].find is not a function

You need Jquery object to apply find:
$($('.primaryResource')[0]).find('.method')
Run

To enforce only top level div is read...
replace this
resource1.querySelectorAll(".resource > .method")
with this
resource1.querySelectorAll(".all > .resource > .method")
here is a sample fiddle.

document.querySelectorAll('.all > .resource:first-child > .method')
This will make sure that the selected <div>s are direct children (not grandchildren) of the <div class="all"> element and only the first resource's methods get selected.

The first selector is really close. You are retrieving all methods that are directly under any resource. Of course, you'd like to retrieve all methods under one specific resource.
I'm not sure which resource you want to select, but in the case you want the first one, you can use the following code:
(plain javascript)
document.querySelectorAll('.resource:first-child > .method').forEach(function(el) {
el.classList.add("selected");
});
$('.resource:first-child > .method').addClass("selected");
(jquery)
Demo: https://jsfiddle.net/2bpfuge4/1/
(function() {
document.querySelectorAll('.resource:first-child > .method').forEach(function(el) {
el.classList.add("selected");
});
})();
.resource,
.method {
width: 100%;
height: 50px;
display: inline-block;
}
.resource {
background-color: green;
padding-left: 50px;
}
.method {
background-color: red;
}
.selected {
background-color: yellow;
}
<div class="all">
<div class="resource">
<div class="method"></div>
<div class="method"></div>
<div class="resource">
<div class="method"></div>
</div>
</div>
<div class="resource">
<div class="method"></div>
<div class="method"></div>
<div class="resource">
<div class="method"></div>
</div>
</div>
</div>
You might need to select a different element. You can use :nth-child to select a specific item, or use multiple > selectors to walk the element tree.

Related

nth-child selector not applied when dynamically inserting two divs [duplicate]

Is there a way to select every nth child that matches (or does not match) an arbitrary selector? For example, I want to select every odd table row, but within a subset of the rows:
table.myClass tr.row:nth-child(odd) {
...
}
<table class="myClass">
<tr>
<td>Row
<tr class="row"> <!-- I want this -->
<td>Row
<tr class="row">
<td>Row
<tr class="row"> <!-- And this -->
<td>Row
</table>
But :nth-child() just seems to count all the tr elements regardless of whether or not they're of the "row" class, so I end up with the one even "row" element instead of the two I'm looking for. The same thing happens with :nth-of-type().
Can someone explain why?
This is a very common problem that arises due to a misunderstanding of how :nth-child(An+B) and :nth-of-type() work.
In Selectors Level 3, the :nth-child() pseudo-class counts elements among all of their siblings under the same parent. It does not count only the siblings that match the rest of the selector.
Similarly, the :nth-of-type() pseudo-class counts siblings sharing the same element type, which refers to the tag name in HTML, and not the rest of the selector.
This also means that if all the children of the same parent are of the same element type, for example in the case of a table body whose only children are tr elements or a list element whose only children are li elements, then :nth-child() and :nth-of-type() will behave identically, i.e. for every value of An+B, :nth-child(An+B) and :nth-of-type(An+B) will match the same set of elements.
In fact, all simple selectors in a given compound selector, including pseudo-classes such as :nth-child() and :not(), work independently of one another, rather than looking at the subset of elements that are matched by the rest of the selector.
This also implies that there is no notion of order among simple selectors within each individual compound selector1, which means for example the following two selectors are equivalent:
table.myClass tr.row:nth-child(odd)
table.myClass tr:nth-child(odd).row
Translated to English, they both mean:
Select any tr element that matches all of the following independent conditions:
it is an odd-numbered child of its parent;
it has the class "row"; and
it is a descendant of a table element that has the class "myClass".
(you'll notice my use of an unordered list here, just to drive the point home)
Selectors level 4 seeks to rectify this limitation by allowing :nth-child(An+B of S)2 to accept an arbitrary selector argument S, again due to how selectors operate independently of one another in a compound selector as dictated by the existing selector syntax. So in your case, it would look like this:
table.myClass tr:nth-child(odd of .row)
Of course, being a brand new proposal in a brand new specification, this probably won't see implementation until a few years down the road.
In the meantime, you'll have to use a script to filter elements and apply styles or extra class names accordingly. For example, the following is a common workaround using jQuery (assuming there is only one row group populated with tr elements within the table):
$('table.myClass').each(function() {
// Note that, confusingly, jQuery's filter pseudos are 0-indexed
// while CSS :nth-child() is 1-indexed
$('tr.row:even').addClass('odd');
});
With the corresponding CSS:
table.myClass tr.row.odd {
...
}
If you're using automated testing tools such as Selenium or scraping HTML with tools like BeautifulSoup, many of these tools allow XPath as an alternative:
//table[contains(concat(' ', #class, ' '), ' myClass ')]//tr[contains(concat(' ', #class, ' '), ' row ')][position() mod 2)=1]
Other solutions using different technologies are left as an exercise to the reader; this is just a brief, contrived example for illustration.
1 If you specify a type or universal selector, it must come first. This does not change how selectors fundamentally work, however; it's nothing more than a syntactic quirk.
2 This was originally proposed as :nth-match(), however because it still counts an element relative only to its siblings, and not to every other element that matches the given selector, it has since as of 2014 been repurposed as an extension to the existing :nth-child() instead.
Not really..
quote from the docs
The :nth-child pseudo-class matches an
element that has an+b-1 siblings
before it in the document tree, for a
given positive or zero value for n,
and has a parent element.
It is a selector of its own and does not combine with classes. In your rule it just has to satisfy both selector at the same time, so it will show the :nth-child(even) table rows if they also happen to have the .row class.
nth-of-type works according to the index of same type of the element but nth-child works only according to index no matter what type of siblings elements are.
For example
<div class="one">...</div>
<div class="two">...</div>
<div class="three">...</div>
<div class="four">...</div>
<div class="five">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
<div class="rest">...</div>
Suppose in above html we want to hide all the elements having rest class.
In this case nth-child and nth-of-type will work exactly same as all the element are of same type that is <div> so css should be
.rest:nth-child(6), .rest:nth-child(7), .rest:nth-child(8), .rest:nth-child(9), .rest:nth-child(10){
display:none;
}
OR
.rest:nth-of-type(6), .rest:nth-of-type(7), .rest:nth-of-type(8), .rest:nth-of-type(9), .rest:nth-of-type(10){
display:none;
}
Now you must be wondering what is the difference between nth-child and nth-of-type so this is the difference
Suppose the html is
<div class="one">...</div>
<div class="two">...</div>
<div class="three">...</div>
<div class="four">...</div>
<div class="five">...</div>
<p class="rest">...</p>
<p class="rest">...</p>
<p class="rest">...</p>
<p class="rest">...</p>
<p class="rest">...</p>
In the above html the type of .rest element is different from others .rest are paragraphs and others are div so in this case if you use nth-child you have to write like this
.rest:nth-child(6), .rest:nth-child(7), .rest:nth-child(8), .rest:nth-child(9), .rest:nth-child(10){
display:none;
}
but if you use nth-of-type css can be this
.rest:nth-of-type(1), .rest:nth-of-type(2), .rest:nth-of-type(3), .rest:nth-of-type(4), .rest:nth-of-type(5){
display:none;
}
As type of .rest element is <p> so here nth-of-type is detecting the type of .rest and then he applied css on the 1st, 2nd, 3rd, 4th, 5th element of <p>.
You may be able to do that with xpath. something like //tr[contains(#class, 'row') and position() mod 2 = 0] might work. There are other SO questions expanding on the details how to match classes more precisely.
Here is your answer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TEST</title>
<style>
.block {
background: #fc0;
margin-bottom: 10px;
padding: 10px;
}
/* .large > .large-item:nth-of-type(n+5) {
background: #f00;
} */
.large-item ~ .large-item ~ .large-item ~ .large-item ~ .large-item {
background: #f00;
}
</style>
</head>
<body>
<h1>Should be the 6th Hello Block that start red</h1>
<div class="small large">
<div class="block small-item">Hello block 1</div>
<div class="block small-item large-item">Hello block 2</div>
<div class="block small-item large-item">Hello block 3</div>
<div class="block small-item large-item">Hello block 4</div>
<div class="block small-item large-item">Hello block 5</div>
<div class="block small-item large-item">Hello block 6</div>
<div class="block small-item large-item">Hello block 7</div>
<div class="block small-item large-item">Hello block 8</div>
</div>
</body>
</html>
All of the questions around using nth-child and skipping hidden tags appear to be redirecting as dupes of this one so I will leave this here. I came across this blog https://blog.blackbam.at/2015/04/09/css-nth-child-selector-ignore-hidden-element/ that uses a clever css approach to make nth-child ignore hidden elements, as follows:
The following CSS adds a margin right to every second visible element no matter which element has the cpw class.
.cpw {
display:none;
}
.video_prewrap {
margin-right:20px;
}
.video_prewrap:nth-child(2n) {
margin-right:0;
}
.cpw ~ .video_prewrap:nth-child(2n) {
margin-right:20px;
}
.cpw ~ .video_prewrap:nth-child(2n-1) {
margin-right:0;
}
Hope that helps someone who is following the dupe trail for the ignore hidden elements questions!
IF you have same parent class for all selector, Then you use that class document.querySelector("main .box-value:nth-child(3) select.priorityOption");
Because in that case document.querySelector("main .box-value select.priorityOption:nth-child(3)"); Not working. Thank You
<div class="card table">
<div class="box">
<div class="box-value">
<select class="priorityOption">
<option value="">--</option>
<option value="">LOREM</option>
<option value="">LOREM</option>
</select>
</div>
<div class="box-value">
<select class="priorityOption">
<option value="">--</option>
<option value="">LOREM</option>
<option value="">LOREM</option>
</select>
</div>
<div class="box-value">
<select class="priorityOption">
<option value="">--</option>
<option value="">LOREM</option>
<option value="">LOREM</option>
</select>
</div>
</div>
</div>
Not an answer to "Can someone explain why?" since other answers has explained.
But as one possible solution to your situation, you may use custom tags for the rows and cells, say <tr-row>, <td-row>, then :nth-of-type() should work. Don't forget to set style display: table-row; and display: table-cell; respectively to make them still work like table cells.

JavaScript/jQuery detect if div contains text excluding children

I need to verify if a DIV has some text or not inside of it BUT NOT inside its children, eg see this example
<div id='one'>
<div id='two'>Abc</div>
</div>
<div id='three'>xyz
<div id='four'></div>
</div>
If I hover/click element one I want to get false (no text), but if i hover element three I want to get true
i tried using
$('#one').text().trim().length > 0
but it seems to check also any children which is want I do not want to happen
This is already answered here: jquery - get text for element without children text
Also mentions using a plugin to accomplish getting only the text of the element and not child elements here: http://viralpatel.net/blogs/jquery-get-text-element-without-child-element/
This meets your requirements
window.onload=function(){
var two = document.getElementById('two').textContent;
console.log(two.trim()=='');
var three = document.getElementById('three').textContent;
console.log(three.trim()=='');
}
<div id='one'>
<div id='two'>Abc</div>
</div>
<div id='three'>
<div id='four'></div>
</div>

css display none on specific page, without page id

I'd like to hide a column in css for only one specific page and i saw several options for it, but every one uses page id. What if two pages have the same id and the differences are only in the class definitions?
I want to use the 'display none' tag only on /Page 2/.
Here is the example:
/Page1/
<body id="body" class="bootstrap-body page-body home_body body-admin-logged" role="document">
/Page 2/
<body id="body" class="bootstrap-body page-body list_page_body category_list_body body-pathway-top body-admin-logged" role="document">
/Column - Page2/
The html code
<aside class="col-md-3 col-sm-12 col-xs-12 column-left">
/Column-Page2/ css code
.column-content-left, .column-left {
float: left;
}
If I use the display none in the css above, it will works perfectly. The problem is that it reflects on /Page1/ too.
Is it possible to do that in css or javascript, without accessing the html?
You can select the body css too like this:
body.list_page_body .column-content-left, body.list_page_body .column-left {
display: none;
}
This should only trigger for the body with the class .list_page_body (or you can use another class specific to that page.
Use a class unique to the second page, for example list_page_body and in your css
.list_page_body .column-content-left, .list_page_body .column-left
{
display:none;
}
It is possible in javascript. Just get the url of the page using window.location.href and add a class or something if it's the page you want the special treatment on.
The classes that distinguish page 2 from page one are: list_page_body category_list_body and body-pathway-top. So you can use any of them to implement your CSS on page 2 without effecting page 1.
Example:
body.category_list_body .column-left{
display:none;
}
You can do it with jQuery by aiming the url path
jQuery(function ($){
var pathname = window.location.pathname;
if (pathname == "/page2/"){
$(".column-class").css("display","none");
}
})
If page 2's body tag has a unique class, you can use the .parent .child {} CSS selector. From what you've provided:
body.list_page_body .column-content-left, body.list_page_body .column-content-left {
display: none;
}
Just so you know, with parent / child selectors, you can use either .parent .child or .parent > .child. The former would select all instances within .parent that the .child class is used in the document:
<body class="parent">
<div class="child">
<div class="child">
</div>
</div>
</body>
In the example above, .parent .child {} would apply rules to both the initial .child as well as the nested .child.
The latter .parent > .child applies to only direct descendants. Using the same example above, only the initial .child element would be selected by .parent > .child. The nested .child wouldn't be affected.

Trying to traverse the DOM so unique video will play when certain div is clicked

So, I have a requirement for dynamically generated content blocks on a page. These blocks have a thumbnail and when it is clicked, it should open a modal, and display an unique overlay window, as well as as the unique associated video.
I am trying to write some generic JavaScript that will traverse the DOM tree properly, so that when any particular thumbnail is clicked, a modal, the associated overlay, and the associated video will open.
Here is an example of what I have now (there are many of these, dynamically added):
<div class="block">
<div class="thumbnail">
//Thumbnail image
</div>
<p>Video Description</p>
<div class="window hide">
<div class="video hide">
//Video content
</div>
</div>
</div>
<div id="modal" class="hide"></div>
and after attempting to do a bunch of different things, I ended up trying to do something like this for the JavaScript, which doesn't work:
$(".thumbnail").on("click",function(){
$("#modal").removeClass("hide").addClass("show");
$(this).closest(".window").removeClass("hide").addClass("show");
$(this).closest(".video").removeClass("hide").addClass("show");
});
CSS is very basic:
.hide { display: none; }
.show { display: block; }
Trying to make the click function generic as possible so it would work on any .thumbnail that was clicked. I've also interchanged find(".window") and children(".window") but nothing happens. Any ideas on what I'm doing wrong? Thanks!
Depending on what you actually want your classes to be, I'd use this code:
$(".thumbnail").on("click", function () {
var $block = $(this).closest(".block");
$block.find(".window, .video").add("#modal").removeClass("hide").addClass("show");
});
DEMO: http://jsfiddle.net/gLMSF/ (using different, yet similar code)
It actually finds the right elements, based on the clicked .thumbnail. It finds its containing .block element, then looks at its descendants to find the .window and .video elements.
If you actually want to include . in your attributes, you need to escape them for jQuery selection.
As for styling, you should probably just have the styling be display: block; by default, and then toggle the hide class. It's less work, and makes more sense logically.
You have a huge issue with your class names in HTML:
<div class=".block">
it should be
<div class="block">
Your modal is the only one that has the class properly named. Your DOM traversals will not work because they are looking for "block" but it's called ".block"
So fix it all to this and you should find more success:
<div class="block">
<div class="thumbnail">
//Thumbnail image
</div>
<p>Video Description</p>
<div class="window hide">
<div class="video hide">
//Video content
</div>
</div>
</div>
<div id="modal" class="hide"></div>
Your code won't work because your selectors have periods (.) in your classes if that's actually what you want, you should try it like this:
$(".\\.thumbnail").on("click",function(){
$("#modal").removeClass("hide").addClass("show");
$(this).closest("\\.window").removeClass("hide").addClass("show");
$(this).closest("\\.video").removeClass("hide").addClass("show");
});
Otherwise just try removing the periods from the classes...
Also, you're using .closest() incorrectly, as it looks up through ancestors in the DOM tree...
You should change your code to:
$(".\\.thumbnail").on("click",function(){
$(this).next("\\.window").children(".video")
.addBack().add("#modal").removeClass("hide").addClass("show");
});

What's the jquery selector for excluding nested descendents?

Per my SO question here, which has turned to jquery to solve this, but which may be worked back into YUI if I get my thinking straight, I need a selector to exclude descendents.
The solution proposed says something like this:
$( '.revealer:not(.revealer > .revealer)' );
To fit more accurately with my situation, because I have multiple HTML chunks to perform the same test on, I have updated it be:
$( '#_revealerEl_0 .handle:not(#_revealerEl_0 .reveal .handle)' );
The HTML its selecting on (image there are numerous copies of this same chunk on a page, each needing to be treated alone - an id attribute is assigned to each 'revealer'):
<div class="revealer" id="#_revealerEl_0">
<div class="hotspot">
<a class="handle" href="javascript:;">A</a>
<div class="reveal">
<p>Content A.</p>
</div>
<div class="reveal">
<p>Content B.</p>
<!-- nested revealer -->
<div class="revealer">
<div class="hotspot">
<a class="handle" href="javascript:;">A</a>
<div class="reveal">
<p>Sub-content A.</p>
</div>
<div class="reveal">
<p>Sub-content B.</p>
</div>
</div>
</div>
</div>
</div>
</div>
In a nutshell: I need to target 'top level' handles within a 'hotspot', per revealer - and no nested descendents with the same class names.
thanks,
d
EDIT:
It's also quite important that I don't start relying on descendant properties like parentNode, childNode[x], nextSibling, etc ... because currently this module is quite flexible in that its 'reveal' and 'handle' elements can reside within other markup and still be targeted - so long as they're found inside a 'hotspot'.
I don't know which is your #_revealerEl_0 element, but if it's your top-level .revealer, can't you just do this?
$('#_revealerEl_0 > .hotspot > .handle')
Or if the top-level .revealer is itself a descendant of #_revealerEl_0, then this works:
$('#_revealerEl_0 > .revealer > .hotspot > .handle')
The basic premise here is that you chain multiple > child combinators.
This works for me using jQuery:
$('.revealer:first > .hotspot > .reveal')
Given the first revealer, find any hotspots that are DIRECT children, and find any DIRECT reveal items within.
So, to assign handlers to your 'handles':
$('.handle').click(function(){
$(this).closest('.hotspot > .reveal').show();
});
The above translates to:
For any given handle, assign a click event function to the element
When a handle is clicked, find its closest parent hotspot
From the hotspot, find any reveal elements that are direct children of the hotspot
Show those elements if they were hidden with display: none.
Try this:
obj = $('.revealer[id*="revealerEl"]');
//this will give you what you are after
result = $("> .hotspot > .handle",obj)
//if you want to see them in red
$("> .hotspot > .handle",obj).css('color','red');
//or assign a click to it
$("> .hotspot > .handle",obj).click(function(){
//blah ....
})

Categories