简体   繁体   English

为什么回调没有检测到 class 实例变量的最新值

[英]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>&#10004;</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如果我理解正确的话,你想

  • Create a nav list that renders a link for each anchor (id) on a page.创建一个导航列表,为页面上的每个锚点 (id) 呈现一个链接。
  • When a target scrolls into view, highlight the associated link and update the location hash当目标滚动到视图中时,突出显示关联的链接并更新位置 hash
  • When clicking on a link in the Navbar, scroll to the target and update the location hash单击导航栏中的链接时,滚动到目标并更新位置 hash

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM