Обновление Force IntersectionObserver

Есть ли способ заставить обновление / запуск 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 это снова. Это может работать для одного элемента, но не в том случае, если у Наблюдателя есть сотни элементов для просмотра.

// 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>

0 ответов

Я не думаю, что можно заставить обновляться наблюдатель пересечения без вызова unobserve / Observation на узле, но вы можете сделать это для всех наблюдаемых узлов, сохранив их в наборе:

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);
        }
    }
}

Изменить: используйте Set вместо WeakSet поскольку они iterableпоэтому нет необходимости проверять, наблюдается ли элемент для каждого элемента в теле. Осторожно звонитьunobseve во избежание проблем с памятью.

Вам просто нужно установить threshold: 1.0для вашего наблюдателя пересечения. Это непростой параметр для понимания. Порог определяет процент пересечения, на котором наблюдатель должен запускать обратный вызов.

Значение по умолчанию - 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>

Я могу неправильно понять вопрос, насколько я понимаю, вы хотите запустить IntersectionObserver, чтобы вызвать ваш обратный вызов. Почему бы тебе не позвонить прямо?

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

Один из способов сделать это - использовать 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>

Другие вопросы по тегам