The code below works as I am able to click a button on the webpage using Python/Selenium/Firefox.
button on the webpage
driver.execute_script('''return document.querySelector('dba-app').shadowRoot.getElementById('configRenderer').shadowRoot.querySelector('ing-default-layout-14579').querySelector('dba-overview').shadowRoot.querySelector('ing-feat-agreement-overview').shadowRoot.querySelector('ing-ow-overflow-menu-14587').shadowRoot.querySelector('button')''').click()
However, some elements are dynamic and the numbers are changing anytime you rerun the script.
The changing elements:
'ing-default-layout-14579'
'ing-ow-overflow-menu-14587'
What must I do to get around the dynamic elements?
One option is to look for other attributes that stay the same across pageloads. For example, given your HTML, you could do:
document.querySelector('#configRenderer') // returns the config renderer element
document.querySelector('[data-tag-name="ing-default-layout"]') // returns the ing-default-layout element
document.querySelector('[data-tag-name="dba-overview]') // returns the dba-overview element
And so on. Or you could the same method to identify a parent or a child, and then navigate to the child or parent respectively.
If the HTML isn't stable enough even for that, another approach would be to search through all elements, and find the one(s) whose tagName starts with what you need.
for (const elm of document.querySelectorAll('*')) {
if (elm.tagName.toLowerCase().startsWith('ing-ow-overflow-menu')) {
// do stuff with elm, which is the overflow menu element
}
}
Related
New to Stack Overflow and Lit.
I'm building something using Lit and wanted to know how do I render element/s only if a parent element is present. I am trying to add a login element dependency which will let user to use these elements only if the login element is present.
Example of what should be allowed.
<login-element>
<child-element-button> </child-element-button>
</login-element>
Example of what should not be allowed.
<child-element-button> </child-element-button>
Another problem that I have is a way for the child elements to share data, it can be to and from sibling elements, children elements, grandchildren element and so on. I do not have a solution for data sharing currently and would like to know the possible ways to achieve this.
Is there a way to achieve all of this without making <login-element> the parent element? something like this?
<login-element> </login-element>
<div> ... </div>
<my-custom-button> </my-custom-button>
<p> ... </p>
<my-colors> </my-colors>
<my-custom-footer> </my-custom-footer>
In short, I need users to be able to use custom elements only if <login-element> if present anywhere in the document and the custom elements that are present should be able to communicate between each other. For instance, a <my-colors> element should be able to send active color data to <display-image> element which will render an image with a specific background color received from `.
Currently, I read the child elements of <login-element>, copy the nodes, run loop, delete original nodes and append with those copied nodes. Additionally, in every child elements, I check if <login-element> is present in DOM or not, if present, render, else render a error element. As for passing and receiving data to and from other components, I have not tried anything.
Than you for your time.
I am attempting to get a list of all elements within a certain class in a table using puppeteer so I can iterate over all of them. Thing is, the table is dynamically loaded when scrolling, so elements will appear/disappear from DOM depending on how far you've scrolled.
Using a simple $$ query does not work and returns the incorrect number of elements, simply what is visible on the page already.
async getAllElements() {
const elms = await this._page.$$('td.class');
return elms;
}
I expect the query to give me all possible elements on the page, but because of the scrolling implementation I am only getting the elements currently visible on the page.
Eventually figured out my own answer.
To get every element in the list, regardless of whether or not it has appeared in DOM yet, use the following:
await page.$eval('#myTableSelector', e => {
//Do something with e.items
});
While this does not return an array of ElementHandles as I had hoped, it does allow me to get all the information I might need, just as if I was using the console in Dev Tools.
This is a follow-up to this question. (Thanks Cyril for all your help!!)
My problem turned out to be a little bit tricker and I still need some help.
When the user clicks on a text element of any node, I would like the following to happen:
For each tree-ancestor of that element:
Append "X" to that text element
Remove the style class "class1" from that element
Add the style class "class2" from that element
Disable the onclick method from that element
During these steps other nodes should remain completely untouched.
I have added the parent item to the Object representing the data in collapse() as Cyril recommended. So I can traverse up the path to root. However as I traverse this path, I cannot get a hold of the d3.js element for the text. How can I do that? Once I do that, the above operations will be relatively simple for me to do.
Below is an illustrated example of what I want. When the user clicks Visualizations, viz and flare should become vizX and flareX. Their style classes should be switched from class1 to class2. And clicking on the vizX and flareX texts should have no effect. And no other nodes should be updated in any way.
How can I do it? I need to be able to grab the d3.js drawing elements as I traverse through the tree.
When you traverse up from the node clicked, to the parent root.
You can also get the DOM element (group which has the circle and text) for the data like shown in the function below.
function findNode(text){
var DOM = d3.selectAll(".node")[0].find(function(node){
return (d3.select(node).data()[0].name == text);
})
return DOM;
}
So now when ever you have the data and you want to find the group it associates to.
You can make use of the above function and call.
var k = findNode("physics");//this will return the DOM which has the node physics.
Hope this helps!
My map application page has a div dedicated to containing a widget, such as a help window or a list of search results, floating above the map. Either a single widget or nothing is shown at any given time. There's a button for each widget, and clicking it either opens that widget (replacing what was in the div) or closes it if it's the widget that is currently open.
This is one of the functions that handles these button clicks (the aforementioned div is widgets.main):
_openSearch: function() {
if (widgets._activeWidget == widgets._searchWidget){
domConstruct.empty(widgets.main);
widgets._activeWidget = null;
} else {
widgets._searchWidget.show();
widgets._activeWidget = widgets._searchWidget;
}
},
This is the function that gets called:
show: function() {
domConstruct.empty(this.rootDiv);
this.rootDiv.appendChild(this.widgetDiv);
},
widget.widgetDiv is the div element with the actual content of the widget. My intention was to generate the content once, then keep it stored in widgetDiv if the widget is closed. This works as expected in IE and Chrome, but IE (IE8 in particular) gets rid of everything inside widgetDiv when domConstruct.empty() is called.
How do I get the desired behavior in IE? Also, which behavior is the "right" one, standards-wise?
Dojo's domConstruct.empty() (see docs here) just destroys all children – it actually sets innerHTML to an empty string for non-SVG elements.
removeChild may be a little less performant but it seems to fit your use case a little better, since it returns the removed nodes. Perhaps something like the following would work:
show: function() {
// save a reference to the current children of rootDiv to use later
var oldChild = this.rootDiv.removeChild(this.rootDiv.firstChild);
this.rootDiv.appendChild(this.widgetDiv);
}
Make sure that rootDiv has exactly one child that you want to save. If it doesn't, you could wrap it in a <div>.
Here's what I'm trying to do: I have a bookmarklet that is looking for elements in the current page (which can be any site) and dispatch a click event on the ones that match. I have that part working.
In some cases though, nothing matches automatically and I want to be able to show (by hovering it) what element should be activated and then save some info about it in localStorage. The next time I'm using the bookmarklet on that page, I want to retrieve that info to identify the element in the DOM and then dispatch a click event.
The question is: what information should I save to be able to identify it? (in most cases, since it will always be possible to create a case where it doesn't work)
In the best case, said-element will have an id value and I'm good to go. In some other cases, it won't and I'd like to see your suggestions as to what info and what method I should use to get it back.
So far my idea is to save some of the element's properties and traverse the DOM to find elements that match everything. Not all properties will work (e.g. clientWidth will depend on the size of the browser) and not all types of elements will have all properties (e.g. a div node won't have a src value), which means that on one hand, I can't blindly save all properties, but on the other, I need to either choose a limited list of properties that will work for any kinds of element (at the risk of losing some useful info) or have different cases for different elements (which doesn't sound super great).
Things I was thinking I could use:
id of course
className, tagName would help, though className is likely to not be a clear match in some cases
innerHTML should work in a lot of cases if the content is text
src should work in most cases if the content is an image
the hierarchy of ancestors (but that can get messy)
...?
So, my question is a bit "how would you go about this?", not necessarily code.
Thanks!
You could do what #brendan said. You can also make up a jQuery-style selector string for each element in the DOM by figuring out the element's "index" in terms of its place in its parent's list of child nodes, and then building that up by walking up the DOM to the body tag.
What you'd end up with is something that looks like
body > :nth-child(3) > :nth-child(0) > :nth-child(4)
Of course if the DOM changes that won't work so good. You could add class names etc, but as you said yourself things like this are inherently fragile if you don't have a good "id" to start with, one that's put there at page creation time by whatever logic knows what's supposed to be in the page in the first place.
an approach would be using name, tagName and className-combination. innerHTML could may be too big.
another approach would be to look for child elements of your choosen element which have an id.
check for id => check for childs with id => check for name, tagName and className-combination (if => tell user to choose a different item :-)
What about finding all elements without an ID and assigning them a unique id. Then you could always use id.
What about using the index (integer) of the element within the DOM? You could loop through every element on page load and set a custom attribute to the index...
var els = document.getElementsByTagName("*");
for(var i = 0, l = els.length; i < l; i++) {
els[i].customIndex = i;
}