22

有没有办法强制更新/运行IntersectionObserver实例?默认情况下,当视口发生更改时,将执行回调。但是我正在寻找一种在其他事件发生时执行它的方法,比如元素的变化。

一个例子:

在初始化时,一切都按预期工作。但是当你改变#red元素的位置时,什么也没有发生。

// elements
let green = document.querySelector('#green');
let red = document.querySelector('#red');

// observer callback
let callback = entries => {
  entries.forEach(entry => {
    let isInside = entry.intersectionRatio >= 1 ? "fully" : "NOT";
    console.log("#" + entry.target.id + " is " + isInside + " inside #container");
  });
};

// start observer
let options = {root: document.querySelector('#container')};
let observer = new IntersectionObserver(callback, options);
observer.observe(green);
observer.observe(red);

// button action
document.querySelector('button').addEventListener('click', () => {
  red.style.right = red.style.right == "" ? "0px" : "";
});
#container {
  width: 100px;
  height: 100px;
  background: blue;
  position: relative;
}

#green, #red {
  width: 50px;
  height: 50px;
  background: green;
  position: absolute;
}

#red {
  background: red;
  right: -10px;
}
<button>move #red</button>
<br /><br />
<div id="container">
  <div id="green"></div>
  <div id="red"></div>
</div>

有什么办法可以使这个工作吗?唯一可行的是unobserve元素并重新启动observing它。这可能适用于单个元素,但如果 Observer 有数百个元素要观察,则不行。

// elements
let green = document.querySelector('#green');
let red = document.querySelector('#red');

// observer callback
let callback = entries => {
  entries.forEach(entry => {
    let isInside = entry.intersectionRatio >= 1 ? "fully" : "NOT";
    console.log("#" + entry.target.id + " is " + isInside + " inside #container");
  });
};

// start observer
let options = {root: document.querySelector('#container')};
let observer = new IntersectionObserver(callback, options);
observer.observe(green);
observer.observe(red);

// button action
document.querySelector('button').addEventListener('click', () => {
  red.style.right = red.style.right == "" ? "0px" : "";
  observer.unobserve(red);
  observer.observe(red);
});
#container {
  width: 100px;
  height: 100px;
  background: blue;
  position: relative;
}

#green, #red {
  width: 50px;
  height: 50px;
  background: green;
  position: absolute;
}

#red {
  background: red;
  right: -10px;
}
<button>move #red</button>
<br /><br />
<div id="container">
  <div id="green"></div>
  <div id="red"></div>
</div>

4

4 回答 4

1

您只需要threshold: 1.0为您的 Intersection 观察者进行设置。这是一个难以理解的参数。阈值定义了观察者应该触发回调的交叉点的百分比。

默认值为 0,这意味着当元素的第一个或最后一个像素与捕获帧的边框相交时,将触发回调。您的元素永远不会完全离开捕获帧。这就是为什么从不调用回调的原因。

如果我们将阈值设置为 1,我们会告诉观察者在元素 100% 在帧内时触发我们的回调。这意味着回调将在这种 100% 包容的状态发生变化时触发。我希望这听起来可以理解:)

// elements
let green = document.querySelector('#green');
let red = document.querySelector('#red');

// observer callback
let callback = entries => {
  entries.forEach(entry => {
    let isInside = entry.intersectionRatio >= 1 ? "fully" : "NOT";
    console.log("#" + entry.target.id + " is " + isInside + " inside #container");
  });
};

// start observer
let options = {root: document.querySelector('#container'), threshold: 1.0 };
let observer = new IntersectionObserver(callback, options);
observer.observe(green);
observer.observe(red);

// button action
document.querySelector('button').addEventListener('click', () => {
  red.style.right = red.style.right == "" ? "0px" : "";
});
#container {
  width: 100px;
  height: 100px;
  background: blue;
  position: relative;
}

#green, #red {
  width: 50px;
  height: 50px;
  background: green;
  position: absolute;
}

#red {
  background: red;
  right: -10px;
}
<button>move #red</button>
<br /><br />
<div id="container">
  <div id="green"></div>
  <div id="red"></div>
</div>

于 2020-11-04T07:56:53.807 回答
1

我认为在不调用节点上的 unobserve/observe 的情况下强制交叉观察者进行更新是不可能的,但您可以通过将所有观察到的节点保存在一组中来执行此操作:

class IntersectionObserverManager {
    constructor(observer) {
        this._observer = observer;
        this._observedNodes = new Set();
    }
    observe(node) {
        this._observedNodes.add(node);
        this._observer.observe(node);
    }
    unobserve(node) {
        this._observedNodes.remove(node);
        this._observer.unobserve(node);
    }
    disconnect() {
        this._observedNodes.clear();
        this._observer.disconnect();
    }
    refresh() {
        for (let node of this._observedNodes) {
            this._observer.unobserve(node);
            this._observer.observe(node);
        }
    }
}

编辑:使用 aSet而不是 aWeakSet因为它们是iterable这样,因此无需检查是否正在为正文中的每个元素观察该元素。调用时要小心,unobseve以免出现内存问题。

于 2020-06-17T09:01:52.540 回答
-1

我可能没有正确回答这个问题,我的理解是你想触发 IntersectionObserver,所以你的回调会被调用。为什么不直接调用呢?

document.querySelector('button').addEventListener('click', () => {
  red.style.right = red.style.right == "" ? "0px" : "";
  callback(red);
});
于 2020-06-21T19:55:23.143 回答
-2

一种方法是使用 MutationObserver。如果发生突变(在这种情况下是样式更改),请在已更改的元素上调用观察/取消观察。这样您就不必对所有元素都这样做。

这是一个例子:

// observer callback
let callback = entries => {
  entries.forEach(entry => {
    let isInside = entry.intersectionRatio >= 1 ? "fully" : "NOT";
    console.log("#" + entry.target.id + " is " + isInside + " inside #container");
  });
};

// start observer
let options = {
  root: document.querySelector('#container')
};
let observer = new IntersectionObserver(callback, options);

const boxes = document.querySelectorAll('#container > div');
boxes.forEach(box => {
  observer.observe(box);
});

// button action
document.querySelector('button').addEventListener('click', () => {
  red.style.right = red.style.right == "" ? "0px" : "";
});



// Mutation observer
const targetNode = document.querySelector('#container');

// Options for the observer (which mutations to observe). We only need style changes so we set attributes to true. Also, we are observing the children of container so subtree is true
const config = {
  attributes: true,
  childList: false,
  subtree: true,
  attributeFilter: ["style"]
};

// Callback function to execute when mutations are observed
const mutationCallback = (mutationsList, mutationObserver) => {
  for (let mutation of mutationsList) {
    if (mutation.type === 'attributes') {
      console.log('The ' + mutation.attributeName + ' attribute was modified.');
      observer.unobserve(mutation.target);
      observer.observe(mutation.target);
    }
  }
};

// Create an observer instance linked to the callback function
const mutationObserver = new MutationObserver(mutationCallback);

// Start observing the target node for configured mutations
mutationObserver.observe(targetNode, config);
#container {
  width: 300px;
  height: 300px;
  background: lightblue;
  position: relative;
}

#green,
#red {
  width: 100px;
  height: 100px;
  background: green;
  position: absolute;
}

#red {
  background: purple;
  right: -10px;
}
<button>move #red</button>
<br /><br />
<div id="container">
  <div id="green"></div>
  <div id="red"></div>
</div>

于 2020-06-19T12:53:19.900 回答