Html injection change properties of element saved by instance

As soon as I try to insert the original HTML text into the body of the document, the saved instance of the element retrieved with .querySelector();

unexpectedly resets the .clientHeight and .clientWidth properties.

The next page shows the problem

<head>

<script>
    window.addEventListener('load', pageInit);

    function pageInit() {
        // saving instance for later use
        var element = document.querySelector('#element')

        alert(element.clientHeight);   // returns 40

        document.querySelector('body').innerHTML += '<p>anything</p>';

        alert(document.querySelector('#element').clientHeight);  // returns 40
        alert(element.clientHeight);  // returns 0
    }
</script>

<style>
    #element {
        height: 40px;
    }
</style>
</head>

<body>

    <div id="element"></div>

</body>

      

Why exactly are the properties of an instance of an atomic variable getting reset? As stated in this question the addEventListener went away after adding innerHTML the eventListeners are detached, but that still doesn't explain why this node element still exists and why its properties were reset from.

+3


source to share


2 answers


You're having an issue where the browser has remelted the document and reset its saved properties. Also known as Thrashing Layout . Accessing certain DOM properties (and / or calling certain DOM methods) will "force the browser to compute style and layout synchronously." This is a quote from Paul Irish for a complete list of what motivates layout / payment . Although innerHTML

not included there, pretty sure it's the culprit.

In your example, the first warning works for obvious reasons. The second works because you are asking the browser to go and find the element and get the property again. The third doesn't work because you are relying on the stored value (which is no longer stored).

The easiest way is to use requestAnimationFrame

when using methods / properties that will force payment.



  window.addEventListener('load', pageInit); 
  function pageInit() {

      // saving instance for later use
      var element = document.querySelector('#element');

      console.log("before:",  element.clientHeight);   // returns 40

      requestAnimationFrame(function() {
        document.querySelector('body').innerHTML += '<p>anything</p>';
      });

      console.log("WITH rAF after:", element.clientHeight);  // returns ~0~ 40!
      
      // out in the wild again
      document.querySelector('body').innerHTML += '<p>anything</p>';
      
      // oh no!
      console.warn("W/O rAF after:", element.clientHeight);  // returns 0 

  }
      

#element {
   height: 40px;
}
      

<div id="element"></div>
      

Run codeHide result


+1


source


It would seem that when you reset an innerHTML

element body

, you are causing the entire page to re-render. Thus div#element

, the one shown on the page is not the same element pointed to by the JavaScript variable element

; rather, the one you see is a new element that was created when the page was re-rendered. However div#element

, that element

indicates that it still exists; it has not yet been destroyed by the browser garbage collector.

To prove this, try replacing the last warning (the one that notifies 0) with console.log(element)

, and then try to click an item in the console window. You will see <div id="element"></div>

, but when you click on it, nothing happens since the div is not on the page.

What you want to do instead is the following:



const newEl = document.createElement('p');
newEl.innerHTML = 'anything';
document.querySelector('body').appendChild(newEl);

      

instead of setting a property body.innerHTML

.

FYI, you should probably switch alert

to console.log

s as notifications annoy people hell and the console is the way to test everything in development.

+2


source







All Articles