Find element by its calculated properties (e.g. position and size withing ranges) [duplicate] - html

This question already has answers here:
Get element with a randomized class name
(2 answers)
Closed 4 years ago.
In an attempt to make web scraping with a headless browser more resilient to site changes, I'd like to combine technical properties of the elements with their visual characteristics.
E.g. when looking for a search bar, I'd like to look for a "big (>50% width), visible (:visible) text input field (<input type="text">) in the upper half of the screen/rendered page." Then, when looking for the submit button, I'd like to find a button located near the aforementioned search bar.
Is there any way to set up this kind of search criterion?
AFAICS, CSS selectors and XPath can only search by predefined parameters (tag, id, class, attributes), not by calculated ones.
The best idea I currently have is to search by predefined parameters, then filter the result further by getting size, position and such for each result and comparing them to the desired ranges. This is rather slow oftentimes since I have to use expressions like *[text()="visible text"] to not rely on technical details that are subject to change without notice.

Here are a few examples of ways to find your wanted element. All below examples are based on the assumption that you have an element that looks a little like this (can be different type and css elsewhere, but basically that you have an element somewhere with some styling and some attribute).
<div mycustomattribute="login" style="width:calc(5cm - 3cm)"></div>
Note that the below examples aren't necessarily all I the ways I can give you, it's just the ones I could think of on the fly, if your problem isn't resolved using these I can probably think of one or two more ways to solve your problem.
Selecting using a custom attribute
You can set any attribute you want on any element you want. For example, if you want <div mycustomattribute="hello"> and then querySelect that, it's totally valid.
var test = document.querySelect("div[mycustomattribute=login]")
The above script will select only the div that has an attribute name with the value login. I think you already know of this method but figured I'd mention it because it's by far the easiest, least hacky way of finding a specific element, if you can set an attribute on your element that is.
Select using position
Lets say you want to select the nearest element that is 50 px to the right of the element you selected.
var base = document.querySelect("div[name=login]")
// Get Y coordinate of base element
var y = base.getBoundingClientRect().top;
// Get X coordinate of base element on its right side, since we're gonna look to the right of it
var x = base.getBoundingClientRect().right;
// Find the element that is 50 pixels to the right of our base element
var element = document.elementFromPoint(x + 50, y);
Select using CSS values
This is more tricky but certainly possible. You are correct in that you can't just run querySelector to find an element based on a CSS value (calculated or otherwise), but you can run the calculation yourself to get the value your desired element should have and then just loop through them to get the one you want.
So, for example:
var divs = document.querySelectorAll('div');
var element = null;
for (i = 0; i < divs.length; ++i) {
/* We assume you know the result of the calculated value, either because it's
a static result (e.g. `5cm - 3cm`), or because you rerun the calculation in
javascript to find out what its result is.
Note that you can use whatever style you want here to find the div, like
"visible" or "display" or whatever you want, just set up the proper if
statements.
*/
if(div.style.width = "2cm") {
element = div;
break;
}
}
References
This is a little side note but try to use mozilla instead of w3schools, mozilla is way better for references. I was hesitant too at first to make the jump to mozillas documentation but it really is way better once you learn how to use it.
https://developer.mozilla.org/en-US/docs/Web/API/Document
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
https://developer.mozilla.org/en-US/docs/Web/API/Document/elementFromPoint
https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/

Related

Find elements with opacity==0 elements and make them visible with Forge

We're trying to visualize an IFC model with forge that contains a lot of invisible elements.
One problem we're facing is that the elements that is invisible does not fall under the normal category of hidden elements. E.g. model.setAllVisibility(true) does not make them appear. Also, model.visibilityManager.hiddenNodes is empty and model.visibilityManager.setVisibilityOnNode(dbId, true) does nothing to make them appear.
What seems to be the case with these elements is that their material.opacity is 0.
We tried setting the opacity to 1, and also tried using a different material, with no luck.
The only thing we have managed to do so far is to highlight the element's corresponding fragment:
model.setHighlighted(fragId, true)
however, this does not really cut it (unless it's possible to highlight many elements, and make them transparent?).
It's also worth mentioning that:
The elements comes from a IfcBuildingElementProxy IFC type
Calling viewer.setDisplayEdges(true) makes it possible to see the wireframe of the invisible elements
It's possible to click on the elements even though they are invisible.
So, my questions:
Is there any good way to find all the invisible elements in a model?
Is it possible to change them to be visible without highlighting them?
The viewer doesn't provide any solution for this out-of-the-box but it should be quite straightforward by putting together some of the viewer's features:
To check the opacity of all objects, you can just get the list of all fragments, and check the opacity of each material:
const frags = viewer.model.getFragmentList();
for (let i = 0; i < frags.getCount(); i++) {
const mat = frags.getMaterial(i);
console.log(mat.opacity);
}
And then, when you find materials you would like to change, you can do that, too. Here's some resources that might help:
how to add custom material to a fragment in forge viewer
https://forge.autodesk.com/blog/material-swatches-forge-viewer
https://forge.autodesk.com/blog/custom-shader-materials-forge-viewer

How to get the length of svg text in Elm

Is there any way to get the length of a piece of Svg text in Elm? I construct text with the text_ function, but need to know its size so I can position other elements.
The sort of function I am looking for would be something like
getLength : Svg.Svg msg -> Int
It's not possible to get a function as you wish because it could't be pure - it would need to return different values depending on the pixel density.
There's also not a version using Tasks as far as I know, and you can't readily get the size of html elements either. You probably need to use a port to get some javascript to do that for you

Setting some css properties according to variables

I want to add a variable to a css class. Is it possile? I mean I want to do something like the following:
#box[i]
{
padding: [i]px;
}
(for example: div which is name "box10" is supposed to get padding of 10px.)
I understand this may not be the right syntax, but I hope you can help me achieve the concept setting my class' properties up to a variable value.
It's currently not possible to use variables within CSS, however there are a number of other options available.
The simplest option would be to create a CSS style for each of your box IDs.
You could use JavaScript to add padding to the box, but it is not sensible to include presentation within logic. In jQuery, a loop to do this would look like (assuming your boxes are ):
$('div[id^=box]').each(function() {
$(this).css('padding',this.id.substr(3)+'px');
});
You could use a pre-processor tool, such as LESS to set variables in your CSS; but you would still need to specify each selector.
Setting padding based on different box ID values seems like an odd problem to have. It may be worth taking a look at whether your approach to building this page is correct. Don't forget every ID on the page should be unique. If you wish to use the same ID on multiple elements, you should use CSS classes.
If you are trying to create a box sized based on a number of results (such as poll results), then it would be easier to use the style attribute and set a width/padding on each element rather than create an ID for every possible outcome. For example:
<div style="width:10px"></div>
No, you can't do that with pure CSS, but you can use less for that.
CSS variables are introduced only in W3C draft at this point and didn't supported by browsers yet.
u could just use javascript, to detect the "name" and then get the substring so u have the number. then just make something like this:
var box = document.getElementByName("box10");
box.style.padding = box.name.substr(3) + "px";

set text and value on div and span

i'd like to set and retrieve 2 values for a div and span
1. text (displayed on screen)
2. value (for backend purposes)
what's the correct way to do this?
there's innerhtml, value, val(), text, html....i'm confused
do these serve different purposes? they seem interchangeable
One correct way to accomplish this is to set a "data attribute" of the div to the internal or server value, with the user-visible value being inside the div:
<div data-info="internal">visible</div>
This shows it done statically. You can also create and set the attributes of a DIV DOM node in JavaScript. Ask me if you want an example.
Sorry for the 8-year delay in answering.
See:
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*

IE <select>, how to make the displayed width the smallest possible for the current options

I've written a function which takes a <select> ID, and replaces all the options with new ones (using jQuery's html()). However, on IE the width of the dropdown remains the original one which has a size way bigger than what I need to display, because is based on the original <option>'s that had long strings captions.
Is there a way to force the <select> to recalculate it's width?
I am using IE 8.
I think the pragmatic solution would be to replace the whole <select> element. This way, the newly inserted one will have the correct width for its <option> elements (disregarding any other styling that may be applied).
The thing to bear in mind if replacing the whole select is that any references to the replaced element will need to cleaned up prior to removing it from the DOM (to avoid any potential memory leaks) and event handlers attached to the newly inserted element.
It's possible to set its width through css.
In your case I would approach it using this method:
If you are using em's or ex's as font size units, then since it is a value based on height, you can assume using a certain ratio that it is almost near the equivalent of a character's width. Based on that, you can actually calculate how wide your select element would be by:
1st: get the number of characters of the smallest string from the select options.
2nd: multiply that by the em value.
3rd: set this value as the select's width.
using jquery that would be achieved through:
var em = 1em; //assign here a value, that corresponds to your layout's font size
var shortest = 6500;
$('mySelect').each(function(i, selected){
temp = $(selected).text().length();
shortest = (temp < shortest) ? temp : shortest;
});
$('mySelect').css('width',(shortest*em + 1) + 'em'); //add 1em for the scrollbar