Search code examples
javascriptjqueryfullcalendarmutation-observersfullcalendar-scheduler

mutation Observer bug depending on Chrome version


I'm using a mutation observer to synchronize the widths of two divs when one is dragged (to resize), in FullCalendar Scheduler.

I'm using the Chrome version: 72.0.3626.109 and all is working. However, when I try on a version below 72.0.36, it makes a bug in my application.

But the mutation observer seems to works for a while... Also, it doesn't seem to work on edge.

Here is the code; maybe I'm doing something wrong:

unifyResourceAreaResize: function() {
   var target = $(".fc-resource-area");
   var observer = new MutationObserver(function(mutations) {
      target.width(mutations[0].target.offsetWidth);
   });
   for (var i = 0; i < target.length; i++) {
      observer.observe(target[i], { 
         attributes: true, 
         childList: true, 
         characterData: true 
      });
   }
}

Here is a codePen CodePen problem

There is 4 .fc-resource-area in total, fullcalendar scheduler has 2 elements (resource header and resource list), and I have in total 2 calendars. It's why I'm trying to synchronize both calendars resource column resize.

Thanks for the help.


Solution

  • Problems in your code:

    • writing the same value to a DOM attribute will trigger the observer so it spins in an infinite cycle and the page freezes.
    • [looking at the full code on codepen] eventAfterAllRender may be calling your unifyResourceAreaResize function every time so a new mutation observer is registered in addition to the old one, for example if the actual DOM wasn't changed.

    Solution:

    • Declare the observer just once.
    • Use the automatically populated document.getElementsByClassName instead of jQuery.
    • Skip updating the width if the difference is less than 1px.
    • No need to observe childList or subtree, all you need is style attribute (see more tips).

    var targets = document.getElementsByClassName("fc-resource-area");
    var observer = new MutationObserver(function (mutations) {
      var width = parseFloat(mutations[0].target.style.width);
      Array.prototype.forEach.call(targets, function (el) {
        if (Math.abs(parseFloat(el.style.width) - width) >= 1) {
          el.style.width = width + 'px';
        }
      });
    });
    
    function unifyResourceAreaResize() {
      for (var i = 0; i < targets.length; i++) {
        observer.observe(targets[i], {
          attributes: true,
          attributeFilter: ['style'],
        });
      }
    }
    

    P.S. If you open devtools prior to running the code, you can click the Pause button in the Sources panel when the page freezes, which will pause the code in devtools so you can inspect it.