2

Okay, so I've a script that gets the CSS property value of a clicked element. For this, I avoided window.getComputedStyle() because it basically converts rem values to px and so on... Instead, I've used CSSStyleSheet.cssRules just to keep the originality of the actual given units and not converting them.

And this does works fine! but it fails to capture CSSRules that are inherited from the parent element since the styles are not directly applied to the element.

For example:

<style>
  #new {
    font-size: 2rem;
  }
</style>

<div id="new">

<h1 class="h1">This is a heading</h1>
<!––Here h1 is inheriting font-size from div that can't be catched by CSSRules––>

</div>

For this case, getComputedStyle() works the best as CSSRules failed to catch the inherited properties but again, the problem of units arises so I cannot use that.

Or maybe something to get the metric unit of an element? ‍♀️ That would do the job too!

Although Chrome seems to have figured this out perfectly:

enter image description here

It's 2022 and I've checked other answers too but seems like nobody has figured this out yet. What's the current solution to this? Or the best hack?

Edit1:

  • I want the same string as defined in the CSS by the user. Like: calc(2rem + 1vh) And with CSSRules it is working but it doesn't works with inheritance because it's not even catching the property! ComputedStyle does that but in px I'm kinda stuck here.

Edit2:

I've chosen Lajos Arpads answer as the accepted one because of the help I recieved in the comments (not the actual answer itself). The solution is going to be labor intensive so it's something I've to do.

Abhay Salvi
  • 890
  • 3
  • 15
  • 39
  • Can you provide a Javascript example as well? – Lajos Arpad Apr 23 '22 at 12:30
  • 1
    Just to be clear, your question is: "how do I get the computed size of an element in the units with which it was defined?" I'm not sure I understand why, since the pixel-size gives an absolute measurement? – David Thomas Apr 23 '22 at 12:30
  • Yeah @DavidThomas – Abhay Salvi Apr 23 '22 at 12:40
  • 1
    Is there always such a 'thing' as 'the units with which it was defined'? calc(10px + 10rem + 10vw) has a bit of a problem knowing what units it was defined in - boiling everything down to px seems like the only way to go. Is there a different way of expressing your requirement? e.g. you want to get the same string as you'd see in the browser (10rem for example, or calc(10rem _ 10vw) etc). – A Haworth Apr 23 '22 at 12:42
  • @AHaworth I want to get the same string. Now CSSRules does the thing of giving the string but it doesn't works on inherited elements – Abhay Salvi Apr 23 '22 at 12:43
  • 1
    OK, thanks for the clarification, maybe good to explain this requirement in more detail in the actual question. – A Haworth Apr 23 '22 at 12:46
  • I've re-edit the question. Thanks @AHaworth – Abhay Salvi Apr 23 '22 at 12:57

1 Answers1

1

You can simply implement a pixel-to-rem converter and use that:

function convertPixelsToRem(pixels) {
    return ((pixels.replace("px", "") / getComputedStyle(document.documentElement).fontSize.replace("px", "")) + "rem");
}

console.log(convertPixelsToRem(window.getDefaultComputedStyle(document.getElementById("new").querySelector(".h1"))["font-size"]));
<style>
  #new {
    font-size: 2rem;
  }
</style>
<div id="new">
  <h1 class="h1">This is a heading</h1>
  <!–– Here h1 is inheriting font-size from div ––>
</div>

EDIT

You could do something like this:

getOriginalRule(ruleName, ruleValue, item) {
    while (hasSameRule(item.parentNode, ruleName, ruleValue)) item = item.parentNode;
    //return the rule value based on the item via CSSStyleSheet.cssRules
}

EDIT2

As per the ideas discussed in the comment section, with the goal being to be able to gather the raw rules applied either directly or by inheritance to nodes, there are two main solutions:

  • one can loop through the rules, find the elements they apply to and then loop the DOM tree to descend the raw rules where they were not overriden
  • one can loop the DOM tree from top to bottom and find all nodes, compare each node's rules with its parent's rules and if they differ, descend the parent's rules

The main difference between the two is that the first is starting with all the CSS rules, while the latter only uses the values.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • Yes! but what if an element uses em or vh or something else? – Abhay Salvi Apr 23 '22 at 12:42
  • Or - a mixture!! – A Haworth Apr 23 '22 at 12:44
  • 1
    @AbhaySalvi If you really want to find out the original metric then you can loop your DOM upwards, to the parent and the parent of the parent and so on until the rule is no longer specified and then use `cssRules` to find the original metric/formula. It's unclear to me what your goal is. My understanding was that you want to get the `rem` value. If instead you want to get the , then you can loop the descendants upwards in the hierarchy until you find the originator. – Lajos Arpad Apr 23 '22 at 12:47
  • @AbhaySalvi so your function would be something of the like of `getMetricUnits(metricName, pixelValue)` and you will have the pixel value from the element and you would get the metric using such a loop. `getMetricUnits` would then receive the parameters and use the correct converter to return the value. – Lajos Arpad Apr 23 '22 at 12:48
  • Is looping through the parents be efficient and not an issue? Like what if the code is big? Thanks @LajosArpad – Abhay Salvi Apr 23 '22 at 12:51
  • 1
    @AbhaySalvi we are speaking about HTML DOM here. A depth of 1000 would be considered very big under these conditions. Note that we are not speaking about the size of the actual HTML, but its maximum depth. However, a loop of 1000 iterations is not considered to be big in programming. Yet, if you do the 1000-stepped loop 1000 times, that could be felt by a user. – Lajos Arpad Apr 23 '22 at 12:53
  • @LajosArpad so basically looping through all the parents is a bad idea? Or maybe. Actually what I'm trying to do is to kinda make a replica of chrome DevTools in terms of style tab. Just like how they get all the values. I wanna know how are they doing it or how can I achieve the same? Just wanted to be clear. Thanks for your support Lajos :) – Abhay Salvi Apr 23 '22 at 14:15
  • 1
    @AbhaySalvi looping through the parents until you find the origin of the rule is not a bad idea. Looping through all the parents is a bad idea, as you should stop as early as you find the origin of the rule. Now, if you do a separate loop for each attribute, that's bad for performance. So, instead of that, here's what you can do: 1. take all the rules you are interested about and 2. loop the parents exactly once until 3. You find the origin of all the rules, this would make sure that you have a single loop for your element. – Lajos Arpad Apr 23 '22 at 14:20
  • @AbhaySalvi However, if you want to know all the rules of all the elements, then you can loop all nodes of your HTML and map each rule with each of them. – Lajos Arpad Apr 23 '22 at 14:21
  • @AbhaySalvi that would prevent doing the same loop of parents for siblings, so it will significantly increase your performance. – Lajos Arpad Apr 23 '22 at 14:21
  • 2
    @AbhaySalvi yet, I'm absolutely sure that this is not how DevTools works. They are surely taking the CSS rules they get from 1. Files 2. Style tags 3. Style attributes 4. Programmatic events, such as style changes via Javascript and they store the actual rule for each item at the time of rule application. So, I'm sure that the raw rules are stored when they are applied, however, you are working with the already applied rules, because the browser's inner events are not available for you. – Lajos Arpad Apr 23 '22 at 14:24
  • 1
    @AbhaySalvi so, you have two main approaches to choose from: 1. You can loop all the CSS rules, apply to all nodes and store the rule strength and order and then loop the inner tree of each node that has rules for it and override the rules of the descendant nodes as long as their rule is of lower priority or 2. Starting from the root node of HTML, you traverse the whole DOM tree once (!) and whenever you reach a node, compare its computed rules with the parent rules. If they are equivalent, then the child inherited the rule. If not, then the rule was overriden. – Lajos Arpad Apr 23 '22 at 14:28
  • @LajosArpad I wish if DevTools provides us with this feature maybe in near future. Thanks for your help. Really appreciate that – Abhay Salvi Apr 23 '22 at 14:28
  • 1
    @AbhaySalvi the only thing you need to beware - as you have already observed - is sizes. So it is recommendable to construct some HTMLs with strange cases, knowing what rule outputs you expect and do some tests for them. Putting these unit tests into iframes would help you to view what works and what not in a glance. But all the ideas described above are labor-intensive. So, the good news is that what you want to do is possible. The bad news is that you need to do a lot of work to achieve it. If my answer has guided you towards a solution, then you may consider accepting it as the correct one. – Lajos Arpad Apr 23 '22 at 14:31
  • 1
    Hey @LajosArpad Just wanted to let you know that I've achieved what I wanted to by your help! I've looped my DOM upwards and got the desired result. Not that of a labor intensive work tho... Also I've used specificity library to find whether the styles are being overriden and their priority too. Thanks a bunch! – Abhay Salvi Apr 24 '22 at 06:44
  • @AbhaySalvi great news! I'm glad to hear that you have been successfully neutralizing the problem. – Lajos Arpad Apr 25 '22 at 11:24