有没有办法找出MutationObserver是断开连接还是观察?

时间:2017-09-01 18:50:49

标签: javascript mutation-observers

this似乎显示了完整的方法列表。

显然找到解决方法并不困难......但为了优雅,应该有一种方法isConnected,不应该在那里吗?

1 个答案:

答案 0 :(得分:1)

如果您真的想知道MutationObserver对象实例的状态,那么一种实现方法就是以合成样式(JSFiddle example)从它创建一个新的对象类:

// to keep actual working methods of the class out of the object's instance whenever possible
// is a good practice, but since ECMAScript2015 (ES2015, ES6) does not support private methods
// (though does support private variables with "var" in constructors), IIFE (Immediately-invoked
// function expression) has to be used to encapsulate, isolate the full class definition:
let UpgradedMutationObserver = (function() 
{
        // private variable/property to store the instance's current status; WeakMap is used to improve memory 
        // management as it sheds its value pairs as soon as the instance is not reference any more:
  const _targetsArrayWeakMap = new WeakMap(), // new WeakMap([[this, []]]) would not work as "this" here is a window object
        // private method-function wrapper that provides access for the object's instance to class prototype
        // methods as well as to an earlier-declared private variable;
        // this wrapper is designed as a function factory as it returns functions that get assigned to the object
        // instance's public methods:
        _callPrototypeMethod = function(prototypeMethod, instance, args)
        {
          // actual type of the private variable/property is set here; runs only once:
          if (typeof _targetsArrayWeakMap.get(instance) === 'undefined')
          {
            _targetsArrayWeakMap.set(instance, []);
          } 
          return function()
          {
            const returnedObject = Object.getPrototypeOf(instance)[prototypeMethod](instance, _targetsArrayWeakMap.get(instance), ...arguments);
            _targetsArrayWeakMap.set(instance, returnedObject.privateVariable);
            return returnedObject.returnValue;
          }
        };
  class UpgradedMutationObserver
  {
    constructor(callback)
    {
      // an arrow function version of the way to attach the object's instance would not need .bind(this)
      // as there is no own "this" in arrow functions, "this" would mean the instance of the object
      this.MutationObserver = new MutationObserver(function( ...args)
      {
        return callback( ...args, this);
      }.bind(this)); 
      this.observe = _callPrototypeMethod('observe', this, arguments); // bind(this);
      this.disconnect = _callPrototypeMethod('disconnect', this, arguments); //.bind(this);
      this.isConnected = _callPrototypeMethod('isConnected', this, arguments); //.bind(this);
      // ... other standard methods like takeRecords() can also taken care of, if necessary
    }
    observe(instance, targetsArray, targetObserve, optionsObserve)
    { 
      // many targets can be observed, though callback function is always the same
      // for the same instance of (Upgraded)MutationObserver:
      instance.MutationObserver.observe(targetObserve, optionsObserve); 
      // before adding targetObserve to the list of observed,
      // it is checked that it exists (at least for now):
      if (document.contains(targetObserve))
      {
        targetsArray.push(targetObserve);
      }
      return {privateVariable: targetsArray};
    }
    disconnect(instance, targetsArray)
    { 
      // the method stops observation of all targets at once
      instance.MutationObserver.disconnect();
      targetsArray = [];
      return {privateVariable: targetsArray};
    }
    isConnected(instance, targetsArray, targetToCheck)
    {
      // in case of observed nodes removed from DOM (destroyed), they are filtered out:
      targetsArray = targetsArray.filter(function(e)
      {
        return document.contains(e);
      });
      // maximum versatily of return results is provided 
      // all while maintaining false/"truthy" quasi-boolean dichotomy:
      return {
                privateVariable: targetsArray,
                returnValue: targetsArray.length == 0
                ? false
                : (targetToCheck
                   ? targetsArray.includes(targetToCheck)
                   : targetsArray)
             };
    }
  }
  return UpgradedMutationObserver;
})();  

console.log('UpgradedMutationObserver: ____________________________________________________________');
const observer = new UpgradedMutationObserver(function(mutationsList, observer)
{
  console.log('UpgradedMutationObserver callback: processing of an added node', mutationsList, observer);
});
console.log('UpgradedMutationObserver initialized as "observer": ', observer);
console.log('UpgradedMutationObserver: observer.isConnected(): ', observer.isConnected()); 

console.log('UpgradedMutationObserver: observer.observe(document.documentElement, {childList: true, subtree: true})...'); 
observer.observe(document.documentElement, {childList: true, subtree: true});

// logs "Array [ html....]"
console.log('UpgradedMutationObserver: observer.isConnected(): ', observer.isConnected()); 

console.log('UpgradedMutationObserver: observer.observe(document.body, {childList: true, subtree: true})...'); 
observer.observe(document.body, {childList: true, subtree: false});

// logs "Array [ html. ..., body. ...]"
console.log('UpgradedMutationObserver: observer.isConnected(): ', observer.isConnected()); 

// logs "true":
console.log('UpgradedMutationObserver: observer.isConnected(document.documentElement): ', observer.isConnected(document.documentElement)); 

var div = document.querySelector('div'), childDiv, i;
for (i = 1; i < 5; i++)
{
  setTimeout(function()
  {
    if (this < 4)
    {
      childDiv = document.createElement('div');
      div.appendChild(childDiv);
      console.log("a child DIV is added after " + this + "s delay for UpgradedMutationObserver to discover");
    }
    else
    {
      console.log('UpgradedMutationObserver: observer.disconnect()...'); 
      observer.disconnect();
      // logs "false" as nothing is observed:
      console.log('UpgradedMutationObserver: observer.isConnected(): ', observer.isConnected()); 
    }
  }.bind(i), i * 1000);
}