I'm trying to click on one value (odds) based on the name of the other element but those two need to be inside a specific parent element which I get by the text inside it.
The snippet below can be found multiple times on the same page with the same classes so targeting by class is not an option.
I first need to get a container with text "1st Goal. Then I need to get it's parent and in the parent, I need to get the second div element (class parent2). That element holds other elements. Let's imagine I need to get the element of value 200 and click on it.
I've tried using parentElement, parentNode but always get the 'undefiend' when getting a parent of the child element, although the child element is retrieved successfully. I just can't get the parent from where I could go down the tree to the desired element and click on it.
<div class="group ">
<div class="parent1 "><span>1st Goal</span></div>
<div class="parent2">
<div class="container ">
<div">
<div><span>Malaga</span><span class="odds">200</span></div>
<div><span>No 1st Goal</span><span class="odds">300</span></div>
<div><span>Las Palmas</span><span class="gll-odds">400</span></div>
</div>
</div>
</div>
<div></div>
</div>
XPath expressions
If you are okay with using XPath expression, you can use the following statement:
//div[contains(#class, "group") and contains(., "1st Goal")]/div[#class="parent2"]//span[#class="odds"]
This XPath expression queries for a div element having the class group and containing the text 1st Goal somewhere. Then it will query the children div with the class parent2 and query span elements with class odds inside.
Usage within puppeteer
To get the element with puppeteer, use the page.$x function. To click the element, use elementHandle.click.
Putting all together, the code looks like this:
const [targetElement] = await page.$x('//div[contains(#class, "group") and contains(., "1st Goal")]/div[#class="parent2"]//span[#class="odds"]');
await targetElement.click();
const parent_node = await child_node.getProperty('parentNode')
You can try this one
Related
I have an object that was retrieved from this expression:
const element = document.querySelector("...my selector...");
I need to get all child elements that have certain attributes, The only way I know to get all children is by:
const children = Array.from(element.childNodes);
but now each child in children is not an element, rather a node, hence, I cannot use getAttribute('') on them;
How do I "cast" a Node to an Element?, Or is there a better way to do this?
How do I "cast" a Node to an Element?
You can't.
Elements are a subset of Nodes.
If it isn't an Element already, then you can't turn it into one.
Consider:
<div>Hello, <strong>World</strong></div>
You have two child nodes. The text node "Hello, " and the strong element node.
It doesn't make sense to treat "Hello, " as an element.
Consider using children instead of childNodes. It fetches only element children.
I need to get all child elements that have certain attributes
In that case, you're probably better off just using a selector which gets you that in the first place. You'll need a child combinator and an attribute selector in addition to your existing selector. Then you'll need to use All to get more than one result.:
document.querySelectorAll("...my selector... > [someAttribute]"
You said you want to select all children with a specific attribute. So select them with querySelectorAll using an attribute selector.
var elems = document.querySelectorAll("#theParentSelector > [theChildsAttribute]")
console.log(elems.length)
Array.from(elems).forEach( function (el) {
console.log(el.getAttribute("theChildsAttribute"))
});
<div id="theParentSelector">
<div theChildsAttribute="1">Foo 1</div>
<div>Bar</div>
<div theChildsAttribute="2">Foo 2</div>
<div theChildsAttribute="3">Foo 3</div>
</div>
You'd use children to gain access to all HTML based nodes:
document.querySelector("...my selector...").children
Suppose I have some HTML as so,
<div id="first"></div>
<div id="second"></div>
and using JavaScript and JQuery I attempt to do the following
var $child = $("<span id='child'>Hello</span>");
$("#first").append($child);
$("#second").append($child);
Will I have two copies of the child node, or will I have two references to the same child node?
Update
I realise that my example creation of a child element is a bit wrong. Perhaps
var $child = $("<span/>").text("Hello");
is a bit more correct.
Child appended in the #first element will get moved to #second element leaving #first element empty. You can use clone() to insert a copy of the child element.
However, there should not be multiple elements with the same ID in DOM, so please change ID to Class for the child element
I have the following markup
<div class = "general">
<div id ="custom"></div>
</div>
How to change id = "custom" in all <div> with class="general" from href on page using jQuery?
You can try this:
$("div.general").each(function() {
$(this).children("div#custom").text($(this).children("a").attr("href"));
});
If I understand you correctly, you want to iterate through all div.generals, and change the text of each child div#custom to the href of the child a.
See a working example on JSfiddle.
Also, another tip is to avoid using multiple elements with the same id. In your code you have a <div> with id="custom". You also say that the div.general appears multiple times — therefore, the id "custom" will appear multiple times. This is bad practice. I suggest that you change id to class.
You need to loop through all div.general and replace the id attribute of div#custom to whatever is there as the anchors href property. The following code will work:
$(".general").each(function(){
$(this).find("#custom").attr("id", $(this).find("a").attr("href").replace("#", ""));
})
Here the .find() will dig out elements from any depth inside the parent. If you are sure about the DOM position of the elements, you can change the .find() to .children()
Basically I want to be able to select the div level2 parent from the child level4 div. My application does not has such classes, otherwise I'd just select level2 :)
<div class="level1">
<div class="level2">
<div class="level3">
<div class="level4"></div>
</div>
</div>
<div class="level2"> <!-- this is hidden -->
<div class="level3">
<div id="start" class="level4"></div>
</div>
</div>
</div>
I start with $('#start') and search for the first parent which is visible, but I'm not seeing a way to return the child of that parent. Searching for $('#start') inside the parent seems very wasteful as I start with a sub child to begin with.
$('#start').closest(':visible') // returns level1
$('#start').closest(':visible').first() // returns the first level2. I can't just use second because the number of level2s can change.
$('#start').closest(':visible').children().each(function(){ /* do some search to check it contains `$('#start')` }) // seems very wasteful.
Another way to look at what I'm trying to say would be; start in the middle, find the outside (the visible element), and move one element in.
How about this:-
$('#start').parentsUntil(':visible').last();
This will give you all hidden parent div's until its visible parent and last() wil give the outermost parent which is hidden. last is not a selector on position it is the last() in the collection.
You want the .has() method
Description: Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element.
$('#start').closest(':visible').children().has('#start');
See fiddle for example.
You say that the classes don't exist...why not add them? It would make thinks much easier to find. The class names don't need to have actual styles associated.
var allLevel4 = $('#start').closest(':visible').find('.level4');
var firstLevel4 = $('#start').closest(':visible').find('.level4')[0];
var secondLevel4 = $('#start').closest(':visible').find('.level4')[1]; //also, #start
Use .filter():
$('#start').closest(':visible').children().filter(':first-child')
.find() is also good for selecting pretty much anything.
<div class = ui-dialog-abc ui-dialog-xyz>
<div id = "sheet1abc">
</div>
</div>
<div class = ui-dialog-abc ui-dialog-xyz>
<div id = "sheet1xyz">
</div>
</div>
<div class = ui-dialog-abc ui-dialog-xyz>
<div id ="sheet2foo">
</div>
</div>
<div class = ui-dialog-abc ui-dialog-xyz>
</div>
Can I select a div based on what div it contains? I want to make the div containing the div whose id contains sheet1 visible or hidden.
If I've understood you correctly, you are looking to select the div that is a parent of a div with an id beginning with "sheet1".
If that's correct, you can do the following:
$("div[id^=sheet1]")
That will select all div elements with an id beginning with "sheet1". You can then loop through the set of elements using each and get the parent of each element to access the parent div.
Once you have the parent div, you can show/hide it using show or hide.
See an example fiddle in which I alert the id of the each matching parent div.
Update
If the child div you are looking for is not a direct child of the ancestor div, you can use parent().closest('div') to climb the DOM tree to find the first ancestor div of the div with your id.
The question asks to get the "div containing the div...", so this method will get the first ancestor div. See this fiddle, in which the child div is contained within a table.
$('div').has('div[id="sheet1abc"]').text();
$('div').has('div[id="sheet1xyz"]').text();
Grab all divs whose ids contain 'sheet1', then grab their closest containing div:
$('div[id*=sheet1]').map(function() {
return $(this).parents('div:first')[0]
});