Does touching the DOM trigger a reflow and repaint even if nothing changes?
Asked Answered
E

1

6

I am working on a small JavaScript template engine, and I have two possible approaches for dealing with updates to the DOM when the model changes:

  1. Check if the DOM update is really needed before doing it. This has the benefit of not risking unnecessary updates, but I am wasting space on keeping track of old values.

    if (oldValue !== newValue) {
        element.textContent = newValue;
    }
    
  2. Just do it. This is obviously simpler, but I am afraid that I will be triggering repaints and reflows for no reason.

    element.textContent = newValue;
    

Note that I am also manipulating the DOM by calling setAttribute, addClass and removeClass, plus setting style[prop] = value.

So, my question is: Are modern browsers smart enough to notice that nothing actually changed, and therefore not run reflow or repaint, if you touch the DOM without actually changing anything?

Elgon answered 3/1, 2017 at 22:37 Comment(3)
Browsers are very smart nowadays. No reflow will happen if there's no change, display wise. And even then, the repaint will only be applied to the portion of the screen being affected.Phillisphilly
I am wasting space Yes, space is at a premium these days, my desktop only has 32GB of RAM.Kimes
@torazaburo Keeping a copy of all the bound values in a large SPA is costly on a mobile device, and if they change often it produces lots of garbage that needs to be collected, messing up framerates. It's not a big effect, no, but if there is zero point in doing it there might still be something to win here.Elgon
P
6

Using the MutationObserver api you can detect DOM changes.

Here is an example you can use to see if a browser triggers the Dom Changed event, based on what you want.

You have here both a text('...') by jquery and an el.textContent (that doesn't use jquery).

$(document).ready(function() {
  $('#btn1').click(function() {
    console.log('text changed - jquery');
    $('#a1').text('text 1');
  });
  $('#btn2').click(function() {
    console.log('text changed - textContent');
    $('#a1')[0].textContent  = $('#a1')[0].textContent 
  });
  $('#btn3').click(function() {
    console.log('class changed');
    $('#a1').attr('class', 'cls' + Math.floor(Math.random() * 10));
  });
});


var target = $('#a1')[0];

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  var changed = false;
  mutations.forEach(function(mutation) {
    // You can check the actual changes here
  });
  console.log('Dom Changed');
});

// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };

// pass in the target node, as well as the observer options
observer.observe(target, config);
.cls1 {
  border: 1px solid red;
}
.cls2 {
  border: 1px solid pink;
}
.cls3 {
  border: 1px solid cyan;
}
.cls4 {
  border: 1px solid darkgreen;
}
.cls5 {
  border: 1px solid orange;
}
.cls6 {
  border: 1px solid darkred;
}
.cls7 {
  border: 1px solid black;
}
.cls8 {
  border: 1px solid yellow;
}
.cls9 {
  border: 1px solid blue;
}
.cls10 {
  border: 1px solid green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="a1" class="cls1">text 1</div>
<button id="btn1">Change text - jquery (keep original)</button><br />
<button id="btn2">Change text - textContent (keep original)</button><br />
<button id="btn3">Change class (real change)</button>
  • In Chrome 55, only setAttribute() and jQuery text() triggered the Dom Change event.
  • In Firefox 50, everything triggered the Dom Change event.
  • In Edge 38, everything triggered the Dom Change event.
Pisgah answered 3/1, 2017 at 22:52 Comment(5)
Cool answer! I updated with some results. One thought though: No dom change event implies no repaint/reflow. But does a dom change event imply a repaint/reflow?Elgon
Regarding the repaint/reflow - I'm really not sure about it (still searching), but here is a test you can try - take an element with a lot of text (start with 1M chars, you can go up afterward), and use the $(el).text($(el).text()). It will trigger the DOM, because it's by jQuery - check if the browser takes time/cpu/scroll changes to do this.Pisgah
Did some testing in FF - discovered that the performance monitor tells you when it repaints and reflows. For small texts (1K charas) it triggers a repaint sometimes, but not reflow. For large texts (1M chars) it triggers an expensive reflow and repaint every time.Elgon
Thats interesting. Did you use both jquery and textContent approaches?Pisgah
No, only textContent as I am keeping away from jQuery anyway.Elgon

© 2022 - 2024 — McMap. All rights reserved.