4

During a transition, is there a way to see what the target value is for a style rule which is under transition? window.getComputedStyle gets the interpolated value, and element.style only looks at the style attribute (I think).

Here's a demo of the problem; I'd like to retrieve the target height value of 1200px during the transition:

https://jsfiddle.net/EoghanM/xz5s3ua6/5/

setInterval(function() {
  document.body.children[0].innerHTML = getComputedStyle(document.body.children[0])['height']
}, 300)

setTimeout(function() {
  document.body.children[0].classList.toggle('changing')
}, 1000)
div {
  height: 400px;
  border: 1px solid red;
  width: 200px;
  transition: height 100s linear;
}

div.changing {
  height: 1200px;
}
<div></div>
mplungjan
  • 134,906
  • 25
  • 152
  • 209
EoghanM
  • 20,021
  • 21
  • 80
  • 110

2 Answers2

1

How about using getComputedStyle on a new instance of changing class?

You can create a div with the class changing and then use getComputedStyle to get the class properties (considering that the height of changing class will be the final height after the transition of your div) like this:

<div class="changing" id="new-changing-div"></div>

and get it's properties:

const element = document.querySelector('#new-changing-div');
const heightAttribute = window.getComputedStyle(element).getPropertyValue('height');
Liad Yogev
  • 776
  • 5
  • 15
  • I am not getting the computed style of the transitioned element, but a new instance of a `changing` class div – Liad Yogev Nov 14 '19 at 19:11
  • Oh yes, I see now... But what about an element, that has an applying CSS declaration other than a class (e.g. `.changing:first-child`)? – FZs Nov 14 '19 at 19:15
  • I mean how would you create a second element (clone) for a custom selector? – FZs Nov 14 '19 at 19:26
  • 1
    Yep, it would defiantly be a problem. We might not use such of css declarations for the transitioned elements in the first place, but that makes the solution not the best practice. – Liad Yogev Nov 14 '19 at 19:38
  • This looks great too; maybe an idea would be to manually override the transition duration to be `0s` on the cloned element? I'm a bit worried about the cost of creating a clone of the element too but would have to measure that. – EoghanM Nov 15 '19 at 09:56
1

I'm 99% sure all of the DOM properties and methods for requesting the element's height (clientHeight, offsetHeight, getBoundingClientRect, etc.) will only give you the interpolated value. Another solution may be to read the value from the CSS stylehseet itself using the CSSOM.

In the code below, we search through the document's stylesheets checking if the selector exists in a rule declaration and if it does, return the value of the property we're looking for. You can console.log() various parts of the stylesheets and rules below to see how the browser stores the information as objects.

Of course this is a simple example based on a simple test case. There could be multiple rules using the same selector, but this will only find the first occurrence. The solution would need to be more robust to find the exact rule you're looking for.

function getCssRuleValue(selector, property) {
  const styleSheets = document.styleSheets;

  let styleSheetsLen = styleSheets.length;

  while (styleSheetsLen--) {
    const styleSheet = styleSheets[styleSheetsLen];
    const rules = styleSheet.rules;
    
    let rulesLen = rules.length;
    
    while (rulesLen--) {
      const rule = rules[rulesLen];

      // The passed-in selector text is found in the rule text
      if (rule.cssText.indexOf(selector) > -1) {
        return rule.style[property];
      }
    }
  }

  // The selector/property was not found in any document stylesheets
  return -1;
}

setInterval(function() {
  document.body.children[0].innerHTML = 
    getComputedStyle(document.body.children[0])['height']
    + '<br>' +
    getCssRuleValue('.changing', 'height')
}, 300)

setTimeout(function() {
  document.body.children[0].classList.toggle('changing')
}, 1000)
div {
  height: 400px;
  border: 1px solid red;
  width: 200px;
  transition: height 100s linear;
}

div.changing {
  height: 1200px;
}
<div></div>
skyline3000
  • 6,896
  • 1
  • 20
  • 32
  • This looks like the right direction for a solution.. The only thing I'm worrying about is (as you mentioned) the precedence rules; I imagine that'd be a nightmare to do manually. (the browser already does this so I was hoping for an answer describing some API that I'd missed) – EoghanM Nov 15 '19 at 09:54
  • @EoghanM I don't think it's too bad, but largely depends on how coupled you want your JS and CSS. You could easily change the above solution to look for `'div.changing'` and also check that the rule text has the same text length, then you know it's an exact match. But if you ever change your CSS to just `.changing` or something else, you also have to update the JS. At that point, I'd say maybe you should just be creating these CSS rules in JS and inserting them into a stylesheet (you can use the CSSOM to do that too). Then you could have a variable holding what you want the value to be. – skyline3000 Nov 15 '19 at 14:43