Is it possible to get the shape/outline of a DOM node in JavaScript? I would like to get some kind of representation of the shape or outline so that I can tell exactly which pixels the DOM node occupies. I can calculate the rectangle using width, height, and position, but this doesn't take into account the border-radius property, among others.
I know that I have access to all of the individual properties that will determine the rendered outline of a node (height, width, border-radius, etc), so I could, in JavaScript, reproduce the calculations the browser does. However, this would be pretty tedious and there would be a lot of edge cases.
and don't forget that different browsers may render slightly different.
That is if you are interested in the padding and margin as well.
Related
I'm trying to calculate an svg's viewBox coordinates of a click event using
const { x, y } = new DOMPoint(event.clientX, event.clientY)
.matrixTransform(svg.getScreenCTM().inverse());
My svg lives inside a container that is CSS-transformed (translate). In Chrome everything is working fine, in Firefox the correct viewBox coords are calculated only if I remove the CSS-transform.
What can be done? Any help would be much appreciated.
The different behaviour you are seeing in Chrome is probably due to it being a little more advanced in its support of SVG 2 specification changes.
In the definition for getScreenCTM in the SVG 2 specification it says that among the transforms to be considered is included:
any transforms from the SVG viewport's coordinate space to the document's viewport, i.e. taking into account the positions of all of the CSS boxes from the outermost svg element's box to the initial containing block when the SVG document fragment is inline in an HTML document
This language is not in the SVG 1.1 specification.
This (the missing SVG2 behaviour) is a known bug in Firefox. You can view the bug report here.
As for a workaround, I don't think there is an easy answer. AFAIK there is no equivalent of getScreenCTM() for HTML elements. You could read the container element's transform property, parse it, then multiply it with the matrix you get from getScreenCTM(). But you probably also need to take account of the page offsets of the container and all its ancestors.
Problem
I was planning on using SVG as an infinite canvas on which I can move around using viewBox.
Turns out that when the viewbox reaches a specific sweetspot, the shapes start disappearing.
Example
Watch how the shapes always stay inside of the view box, until the viewbox passes the sweetspot:
Live reproduction
It's as if SVG has a limitation mismatch where the viewbox can keep running freely, but shapes can only render within specific bounds.
Research
Furthermore, the sweetspot is different between browsers:
On Chrome it's 33554399.
On Firefox it's 17895590.
The same happens on the other direction. Apply a minus sign to the above values and the shapes disappear on the other side of the canvas.
My best guess is that the browser implementation of SVG is internally constrained by something parallel to Number.MAX_VALUE (but is significantly lower).
However, I couldn't find any relevant documentation on the subject.
Question
Any way to fix or work around this? I'm hoping to keep my current implementation and not simulate the viewbox on my own.
Sorry for the vague title, but I'm not exactly sure what to call this. I'm making an assignment planner using HTML and JavaScript, and I've ran into a bit of a problem while trying to make a feature to snap your text to the nearest line.
The planner itself looks like this
The "Snap to Line" feature works by checking the Y coordinate of the note (which is an input element) and checking the nearest line. I know it's not good practice, but to make it easier everything is a fixed size, so I have an array of every line's Y coordinate it could snap to:
[58,80,102,124,146,168,190,212,234,257,279,301,323,345,367,389,411,434,458,480,502,524,546,568,590,612]
When the user creates a note or change's its position, this bit of code runs:
let height = parseInt(window.getComputedStyle(e.target).getPropertyValue('height'));
e.target.style.top = getClosest(yCoord+(height/2))-(height/2)
So it gets the element's height, then finds the Y coordinate of the closest line and subtracts half the height of the element to find the proper coordinates.
I arbitrarily used height/2 as opposed to height. I thought that since the coordinate is of the line, it would need to be exactly height higher up, which makes perfect sense, but in practice for whatever reason that didn't work, and height/2 was able to give me better, somewhat better results.
However, if I use the function, it doesn't exactly work. I've tried many different methods but I can't seem to figure it out. This is the result. As you can see, it works pretty well for smaller heights, but it's as if there's an offset relative to the height of the element that affects the position. Is the problem because my method is terribly wrong? (probably) Or is there just some factor that I've been forgetting?
There aren't any borders, no margins, the padding is only 1px, but that shouldn't matter anyway since I use getComputedStyle (which I believe takes everything into account) to find the height and not the font size.
The code surrounding this feature is only a small part in a decently sized project, but I'm posting the entire document so that it may be easier to fix this.
Thanks!
note: the window with the planner on it is a popup, so your browser might block it
The code (too many characters for this post so it's on pastebin)
EDIT: I think I found the problem, no surprise that I found it just as I posted this. First of all, I needed to use height and not height/2 (no surprise there, makes sense). I though I needed to use height/2 because I was testing with small heights and the snap points were a few pixels off (7, to be exact) which made it seem like height/2 was better. Two, the main problem, is that since I used height instead of font-size, the height was the height of the entire element which meant that the bottom of the P hit the line instead of going past it.
I am wondering if JS is slower in determining certain values than CSS, because they both would have to do the same calculatios.
In other words, if I set a margin in % to an element, it will use the width of its parent as a base. For instance, margin: 50% = margin: 0.5 * parent.width. Internally, then, the browser has to calculate the correct margin based on the parent's width, right? How then, is this different from calculating in JS? Why is CSS faster? What internal CSS rendering makes these computational processes faster than JS can do?
Here is a fiddle. Both child divs are the same, but one div's margin is calculated in CSS (margin: 20%) and the other in JS:
var $cont = $("#container");
$("#js").css("margin", $cont.width() * 0.2);
Considering resize: the CSS engine will have to re-calculate the margin as well on resize, right?
Considering load time: I am only talking about the actual execution time. In other words:
var $cont = $("#container");
$("#js").css("margin", $cont.width() * 0.2);
vs.
#css {margin: 20%;}
Excluding any additional (library) load times. The difference between jQuery and vanilla JS shouldn't be included in the answer. I am aware of performance differences between the two.
This question will have somewhat of a vague answer since it's extremely browser-dependent. However, it's probably fairly safe to say that CSS is generally going to be faster here.
The first thing to note is that we are not comparing the speed of CSS as a language to JavaScript, per se. CSS isn't necessarily executed repeatedly in this sense. Its focus is on specifying properties that are very native to the heart of the browser. The browser can parse it once and then do whatever it wants with that specification. In fact, it would generally be a rather poor browser if it's repeatedly checking/executing the same unchanging CSS code over and over.
So one way to look at this is that we aren't really asking why CSS is faster than JavaScript. CSS is not the one doing the calculations here. We're asking why a native web browser can be faster than the client-side JavaScript it can run on top.
If you put yourself in a browser developer's shoes, you can see a specification when parsing the CSS file initially that an element is supposed to have a relative size that is 50% of its parent.
You're now allowed to do whatever you want with the specification as the page is redrawn, scrolled, interacted with. You can store that size specification at the heart of your core data structures and even use metal-scraping assembly code if you want. You can calculate the new absolute sizes of such relative-sized children in a single pass descending down the hierarchy from parent to child. You can calculate the absolute pixel sizes of 4 children at once using SIMD. You can do anything given that initial property specification.
With JavaScript, we have no such control. The client-side script can do anything here, so we have to trigger a resize event and if the JS side resizes things inside a resize event, it might trigger a whole cascade of such events and possibly even reset/interfere with the regular process of calculating client rectangles. The size of an element, even at the percentage level, turns into a blank, a giant question mark that has to be repeatedly 'queried' in a sense. This is all speculative as it's very browser-dependent.
But what isn't browser-dependent is that CSS allows those kinds of browser-native optimizations given its static, predictable, property-specifying nature. JS has a lot more freedom, and so it has to run on top and can't have that kind of special privilege of allowing the browser developers to do whatever they want with it.
With CSS, the ball is in the native browser developers' court. With JS, the ball is in your court, and it's going to be quite difficult (if even possible) to beat the browser developers given that the JS code is running on top of the browser.
1) In the end, it's all CSS positioning. Calculating it in JS, versus letting the CSS + layout engine calculate it just means that once you have the number, you need to assign it to the element (or elsewhere) and have the CSS engine pick it up from there, and apply it during the next repaint.
2) The CSS has access, internally, to the actual pixel widths of things. There are times where you need to determine computed styles of elements, in order to make your calculations...
...that means you need to call a method which reads those values, parse them into workable numbers, do your calculations, convert back into the appropriate string representing the unit value, and send it back to CSS/layout/rendering.
This doesn't mean that JS can't be fast.
Likewise, there are things that JS can do, which CSS will never be able to do on its own (like advanced, dynamic animation-blending)...
...but the trick is to use both where appropriate, and for reasons well-described above, to know that something which you write to be run dozens of times a second, can't be compared to something which only needs to be calculated each time you touch something which invalidates the previous calculations, with the benefit of knowing that you can change minute details on a whim in the JS version, as well as compose all kinds of sequences, which can be decomposed and recomposed on the fly, where in CSS, you're at the mercy of the predefined animations.
I have a very complicated site built on CSS3 that has html elements 3d-transformed, rotated, flipped, flopped and just generally distorted.
I'm trying to figure out the on-screen location of one of these elements and don't see any way to do so. I was wondering if anyone has any ingenious ideas.
Alternatively, if anyone can explain the math behind -webkit-perspective, I can figure out the position as that's the only thing I'm not sure how to model.
Have you tried using getBoundingClientRect()?
I've used it successfully in the past to calculate the dimensions of elements that have been transformed with the transform property.
The problem is, that the CSS3 transformations doesn't actually change the position of the elements in anyway. Of course the browsers "know" that they are repositioned, because it renders them, but this information is not provided back to the DOM/API.
The only thing I can think of, is to calculate the positions based on the transformations yourself, since these are "simple" matrix transformations.
Unfortunately Algebra class has been too long ago, that I can't tell you anymore how to do it - only that it is possible.
Using getBoundingClientRect is a good idea but will only give you the coordinates of the rectangle that contains your shape, not the exact coordinates of the 4 topleft, bottomright, bottomleft, topright corners.
You'd only be able to do this by taking each of those non-transformed coordinates and applying the transform via javascript.