1

Given the following DOM structure...

<div id="outer-container">
  <div class="side-panel"><div width="200px"/></div>
  <div id="content"><!-- some content --></div>
  <div class="side-panel"><div width="100px"/></div>
</div>

With the following CSS rules...

#outer-container {
  display: 'flex';
  align-items: 'stretch';
  width: '100vw';
  height: '100vh';
}

.side-panel {
  flex-shrink: 0;
}

#content {
  display: 'flex';
  flex-direction: 'column';
  align-items: 'stretch';
  flexGrow: 1;
}

How can I observe mutations to the width/height of #content?

This is typically done by observing mutations in the style attribute, and checking the bounds of the element (or some other variation) when the style has changed. However, being a flex container & child, I notice that when I inspect the width/height of the element, they are not defined.

Changes to the size of #content does not produce events from my MutationObserver (which uses the following config: {attributes: true, attributeFilter: ['style']})

My use case here is to turn the side panels into drawers when the #content width decreases below some defined threshold. I am working with React, however this seems to be a native html/js problem.

Help greatly appreciated!

PS: I tried setting flex-basis on #content to my chosen threshold, but this didn't help. I wonder if I should define a width instead of flex-basis.. however I'm unsure if this will produce undesired flex behaviour.

sookie
  • 2,437
  • 3
  • 29
  • 48
  • So you want to know the width and height of the `#content` element after flex has modified the element size? – AndrewL64 Jan 17 '19 at 12:14
  • Yes. I would like to listen to changes in width/height anytime flex modifies the element size – sookie Jan 17 '19 at 12:16
  • This feels like an XY problem to me: you shouldn't be subscribing to changes of the computed style of the element. Instead, you should subscribe to events that causes layout changes that, in turn, changes the size of the element. For example, maybe you have a CSS transition/animation that changes its size: then you listen to the `transitionEnd` or `animationEnd` events. If the DOM is being mutated, you check the MutationObserver instead. The reason why watching the `style` attribute doesn't work because it applies to the **inline style attribute**, not the computed style of the element. – Terry Jan 17 '19 at 12:34
  • I was hoping to abstract this behaviour into a pure 'layout' component, rather than couple it to external events. It seems like my best option is to listen to changes in the viewport size instead (which in turn would cause modifications to the width of `#content`). Btw, I didn't realize MutationObservers only observed inline style attributes. Thanks for the info. – sookie Jan 17 '19 at 12:40
  • You need a ResizeObserver or IntersectionObserver, not MutationObserver. – wOxxOm Jan 17 '19 at 13:18
  • Looks like you need [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). – mrReiha Jan 17 '19 at 13:42
  • ResizeObserver has limited support, being an experimental feature – sookie Jan 17 '19 at 13:56

1 Answers1

2

You can either use the offsetWidth/offsetHeight properties, the getBoundingClientRect() method or the getComputedStyle() method inside a function say, checkWidth() then invoke the function whenever an event or function alters your element's dimensions.

For example, a resize event will change your element's dimensions so run this function when a resize event is fired.


Check and run the following Code Snippet then resize your browser or click the button for a practical example of what I have described above:

/* JavaScript */

function checkWidth(){
  let e = document.getElementById("content");
  let widthA = window.getComputedStyle(e).width;
  let widthB = e.getBoundingClientRect().width;
  let widthC = e.offsetWidth;

  console.log("getComputedStyle: " + widthA);
  console.log("getBoundingClientRect: " + widthB);
  console.log("offsetWidth: " + widthC);
};

window.addEventListener("resize", checkWidth); //checkWidth() added since "resize" alters your element's dimensions

function editContent(){
  let e = document.getElementById("content");
  e.style.width = "300px";
  
  checkWidth(); //checkWidth() added since editContent() alters your element's dimensions
}

document.getElementById("btn").addEventListener("click", editContent);
/* CSS */

#outer-container {
  display: flex;
  align-items: stretch;
  width: 100vw;
  height: 100vh;
}

.side-panel {
  flex-shrink: 0;
}

#content {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  flex-grow: 1;
}
<!-- HTML -->

<button id="btn">Click Me</button>
<hr/>
<div id="outer-container">
  <div class="side-panel">
    <div style="width: 200px">Left</div>
  </div>
  <div id="content">Middle</div>
  <div class="side-panel">
    <div style="width: 100px">Right</div>
  </div>
</div>
AndrewL64
  • 15,794
  • 8
  • 47
  • 79
  • This is all assuming that a window resize event is the *only* event that causes the `#content` to change size :) – Terry Jan 17 '19 at 12:48
  • @Terry That's true too. But I can just separate the function from the event listener and call the function when other changes are made too. Let me edit the answer a bit. – AndrewL64 Jan 17 '19 at 12:49
  • Yes, this will work fine. I've handled the other events that can cause `#content` to change size. As for giving this a best answer, should I re-word my question first? The answer doesn't technically solve the issue of monitoring computed style changes in a flex item – sookie Jan 17 '19 at 14:01
  • @sookie Yes, I think rewording your question would be a better route if this solution didn't exactly addressed your problem. – AndrewL64 Jan 17 '19 at 14:49