[英]Why callback not detecting latest value of class instance variable
I have a web component which is basically a class:我有一个 web 组件,它基本上是一个 class:
class NavList extends HTMLElement {
_wrapper;
_observer;
_observerActive = true;
get observerState() {
return this._observerActive;
}
render() {
this._wrapper.innerHTML = "";
const activeList = window.location.hash.slice(1);
const container = htmlToElement(`<nav class="navlist"></nav>`);
for (let list in veritabani) {
container.appendChild(
htmlToElement(
`<a href=#${list} class="nav-entry ${
activeList === list ? "active" : ""
}">${list}</div>`
)
);
}
// prevent observer from changing hash during smooth scrolling
container.addEventListener("click", this.disableObserver);
this._wrapper.appendChild(container);
}
observe() {
let options = {
root: document.querySelector(".check-list"),
rootMargin: "0px",
threshold: 0.4,
};
let observer = new IntersectionObserver(
this.observerCallback.bind(this),
options
);
this._observer = observer;
const targets = document.querySelectorAll("check-list");
console.log("observer target:", targets);
for (let target of targets) {
observer.observe(target);
}
}
observerCallback(entries, observer) {
console.log("observer active?", this.observerState);
entries.forEach((entry) => {
if (entry.isIntersecting && this.observerState) {
const targetListName = entry.target.getAttribute("list");
console.log(entry, targetListName);
window.location.hash = targetListName;
this.render();
}
});
}
disableObserver() {
this._observerActive = false;
console.log("observer disabled", this._observerActive);
function enableObserver() {
this._observerActive = true;
console.log("observer enabled", this._observerActive);
}
const timer = setTimeout(enableObserver, 2000);
}
connectedCallback() {
console.log("hash", window.location.hash);
// wrapper for entire checklist element
const wrapper = this.appendChild(
htmlToElement(
`<span class="navlist-wrapper ${window.location.hash}"></span>`
)
);
this._wrapper = wrapper;
this.render();
setTimeout(() => {
this.observe();
}, 1);
}
// more code below
As you can see, I have an intersection observer and I am trying to disable its callback when an anchor is clicked.如您所见,我有一个路口观察器,我试图在单击锚点时禁用它的回调。
The observer detects elements on the page and changes the URL hash so that the visible element name is highlighted on the navlist
, this works fine but interferes with the function of the navlist
since clicking on navlist
entry should also scroll the page to that element!观察者检测页面上的元素并更改 URL hash 以便可见元素名称在
navlist
上突出显示,这工作正常但会干扰 navlist 的navlist
因为单击navlist
条目也应该将页面滚动到该元素!
My solution is to disable the intersection observer's callback after a navlist
entry is clicked using setTimout
:我的解决方案是在使用
navlist
单击setTimout
列表条目后禁用路口观察器的回调:
disableObserver() {
this._observerActive = false;
console.log("observer disabled", this._observerActive);
function enableObserver() {
this._observerActive = true;
console.log("observer enabled", this._observerActive);
}
const timer = setTimeout(enableObserver, 2000);
}
The above code sets an instance variable to false
after a click on navlist
, the variable changes state to false
but the observer's callback does not see the change and uses the old state which is true
by default.上面的代码在点击 navlist 后将一个实例变量设置为
false
,该变量将navlist
更改为false
,但观察者的回调没有看到更改并使用默认为true
的旧 state。
My Question : Why is this happening?我的问题:为什么会这样? and how can I fix it?
我该如何解决?
I tried delaying the callback function thinking that it is being activated before the state change, but it did not work.我尝试延迟回调 function 认为它在 state 更改之前被激活,但它没有用。
UPDATE : Here is a link to a live demo of what I am doing更新:这是我正在做的 现场演示的链接
I found a solution though I still do not quite understand whats happening.我找到了解决方案,但我仍然不太明白发生了什么。
The solution is to move the flag _observerActive
outside of the class Navlist
:解决方案是将标志
_observerActive
Navlist
:
let OBSERVER_STATE = true;
class NavList extends HTMLElement {
_wrapper;
_observer;
render() {
this._wrapper.innerHTML = "";
const activeList = window.location.hash.slice(1);
const container = htmlToElement(`<nav class="navlist"></nav>`);
for (let list in veritabani) {
console.log(`active? ${list}===${activeList}`);
container.appendChild(
htmlToElement(
`<a href=#${list} class="nav-entry ${
activeList === list ? "active" : ""
}">${list}</div>`
)
);
}
// prevent observer from changing hash during smooth scrolling
container.addEventListener("click", this.disableObserver);
const addButton = htmlToElement(
`<button class="nav-add">
<span class="nav-add-content">
<span class="material-symbols-outlined">add_circle</span>
<p>Yeni list</p>
</span>
</button>`
);
addButton.addEventListener("click", this.addList.bind(this));
this._wrapper.appendChild(container);
this._wrapper.appendChild(addButton);
}
disableObserver() {
OBSERVER_STATE = false;
console.log("observer disabled", this.OBSERVER_STATE);
function enableObserver() {
OBSERVER_STATE = true;
console.log("observer enabled", OBSERVER_STATE);
}
const timer = setTimeout(enableObserver, 2000);
}
addList() {
const inputGroup = htmlToElement(`
<div class="input-group">
</div>`);
const input = inputGroup.appendChild(
htmlToElement(`
<input placeholder="Liste Adi Giriniz"></input>`)
);
const button = inputGroup.appendChild(
htmlToElement(`
<button>✔</button>`)
);
button.addEventListener("click", () =>
this.addNewCheckList(input.value)
);
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
console.log(input.value);
this.addNewCheckList(input.value);
}
});
const addButton = document.querySelector(".nav-add");
console.log(this._wrapper);
this._wrapper.replaceChild(inputGroup, addButton);
}
addNewCheckList(baslik) {
veritabani[baslik] = {};
const checkListContainer = document.querySelector(".check-list");
const newCheckList = htmlToElement(`
<check-list
baslik="${baslik} Listem"
list="${baslik}"
placeholder="�� A��klamas�..."
></check-list>`);
checkListContainer.appendChild(newCheckList);
this._observer.observe(newCheckList);
this.render();
newCheckList.scrollIntoView();
}
observe() {
let options = {
root: document.querySelector(".check-list"),
rootMargin: "0px",
threshold: 0.4,
};
let observer = new IntersectionObserver(
this.observerCallback.bind(this),
options
);
this._observer = observer;
const targets = document.querySelectorAll("check-list");
console.log("observer target:", targets);
for (let target of targets) {
observer.observe(target);
}
}
observerCallback(entries, observer) {
console.log("observer active?", OBSERVER_STATE);
entries.forEach((entry) => {
if (entry.isIntersecting && OBSERVER_STATE) {
const targetListName = entry.target.getAttribute("list");
window.location.hash = targetListName;
this.render();
}
});
}
connectedCallback() {
console.log("hash", window.location.hash);
// wrapper for entire checklist element
const wrapper = this.appendChild(
htmlToElement(
`<span class="navlist-wrapper ${window.location.hash}"></span>`
)
);
this._wrapper = wrapper;
this.render();
setTimeout(() => {
this.observe();
}, 1);
}
}
If I understand correctly, you want to如果我理解正确的话,你想
You don't have to keep track of the IntersectObserver state and you don't have to disable it.您不必跟踪 IntersectObserver state,也不必禁用它。 Just use pushState() instead of location.hash to update the hash. https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
只需使用 pushState() 而不是 location.hash 来更新hash。https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
index.html index.html
<head>
<style>
/* Makes sure that the first section scrolls up enough to trigger the effect */
section { scroll-margin: 20px }
</style>
</head>
<body>
<div class="sidebar">
<nav-bar></nav-bar>
</div>
<div class="content">
<section id="One">
<header><h1>One</h1></header>
<p> A bunch of text</p>
</section>
<!-- A Bunch of Sections -->
</div>
</body>
component.js组件.js
export class NavList extends HTMLElement {
#template = `
<style>
.visible {
background-color: orange;
}
</style>
<menu></menu>
`;
constructor() {
super();
this.shadow = this.attachShadow({mode: "open"});
}
connectedCallback() {
const li = document.createElement('li');
const a = document.createElement('a');
this.tmpl = document.createRange().createContextualFragment(this.#template);
this.menu = this.tmpl.querySelector('menu');
this.anchors = document.querySelectorAll('[id]');
this.observer = new IntersectionObserver( entries => {
const entry = entries.shift();
const id = entry.target.getAttribute('id');
const link = this.menu.querySelector(`a[href="#${id}"]`);
Array.from(this.menu.querySelectorAll('a')).map(a => a.classList.remove('visible'));
link.classList.add('visible');
history.pushState({}, '', `#${id}`);
}, {threshold: 1});
for (let anchor of this.anchors) {
const item = li.cloneNode();
const link = a.cloneNode();
const id = anchor.getAttribute('id');
link.setAttribute('href', `#${id}`);
link.innerText = id;
link.addEventListener('click', evt => this.clicked(evt));
item.append(link);
this.menu.append(item);
this.observer.observe(anchor);
}
this.render();
}
disconnectedCallback() {
this.observer.disconnect();
}
clicked(evt) {
evt.preventDefault();
const target = evt.target.getAttribute('href');
const elem = document.querySelector(target);
elem.scrollIntoView({behavior: "smooth"});
history.pushState({}, '', `#${target}`);
}
render() {
this.shadow.append(this.tmpl);
}
}
customElements.define('nav-list', NavList);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.