MutationObserver удален Узлы прокруткиВысота дочернего элемента равна 0? Как получить старое значение высоты свитка? - PullRequest
0 голосов
/ 08 февраля 2020
  • У меня зарегистрирован наблюдатель мутации в родительском div с divами в качестве дочерних элементов
  • Я показываю только подмножество дочерних элементов на основе startIndex и endIndex
  • Когда я увеличиваю startIndex, элементы удалены, и я хочу, чтобы их высота переводилась соответствующим образом
  • Я получаю их scrollHeight как всегда 0
  • Как мне получить их высоту, как это было до того, как они были удалены

HTML

<script type="text/x-template" id="virtual-list">
  <div id="root" ref="root">
    <div id="viewport" ref="viewport">
      <div id="spacer" ref="spacer" :style="spacerStyle">
        <div v-for="i in visibleItems" :key="i.index" :id="i.index" class="list-item">
          {{i.value}}
  </div>
  </div>
  </div>
  </div>
</script>

<div id="app">
  <button @click="incrementStart">Start +</button>
  <button @click="decrementStart">Start -</button>
  <button @click="incrementEnd">End +</button>
  <button @click="decrementEnd">End -</button>
  <virtual-list></virtual-list>
</div>

CSS

* {
  box-sizing: border-box;
}

html,
body,
#app {
  height: 100%;
}

#app {
  padding: 1.25rem;
}

#root {
  height: 50%;
  overflow-y: auto;
}

.list-item {
  padding: 0.75rem 0;
}

JS

const PAGE_SIZE = 50;

const items = new Array(PAGE_SIZE).fill(null).map((item, index) => {
  return {
    id: faker.random.uuid(),
    index: index,
    value: "Item " + index + " " + faker.random.words(index % 25)
  };
});

const bus = new Vue({});

Vue.component("virtual-list", {
  template: "#virtual-list",
  data() {
    return {
      isMounted: false,
      items,
      startIndex: 0,
      endIndex: PAGE_SIZE,
      scrollTop: 0,
      translateY: 0,
      observer: null
    };
  },
  computed: {
    visibleItems() {
      return this.items.slice(this.startIndex, this.endIndex);
    },
    /**
    Translate the spacer verticaly to keep the scrollbar intact
    We only show N items at a time so the scrollbar would get affected if we dont translate
    */
    spacerStyle() {
      return {
        willChange: "auto",
        transform: "translateY(" + this.translateY + "px)"
      };
    }
  },
  methods: {
    onMutation(entries) {
      for (const entry of entries) {
        if (entry.type === "childList") {
          this.$nextTick(() => {
            if (entry.removedNodes && entry.removedNodes.length) {
              for (let i of entry.removedNodes) {
                console.log(
                  entry.removedNodes.length,
                  "removed",
                  i.scrollHeight,
                  i.offsetHeight,
                  i.clientHeight,
                  i.getAttribute("data-height"),
                  i
                );
                this.translateY += 42;
              }
            }
          });

          if (entry.addedNodes && entry.addedNodes.length) {
            for (let i of entry.addedNodes) {
              console.log(
                entry.addedNodes.length,
                "added",
                i.scrollHeight,
                i.offsetHeight,
                i.clientHeight,
                i.getAttribute("data-height")
              );
              this.translateY -= 42;
            }
          }
        }
      }
    },
    handleScroll() {
      this.scrollTop = this.$el.scrollTop;
      this.startIndex = Math.floor(this.scrollTop / 42);
      // this.endIndex = Math.ceil((this.scrollTop + this.$el.offsetHeight) / 42)
      // console.log(this.scrollTop, Math.floor(this.scrollTop / 42), Math.ceil((this.scrollTop + this.$el.offsetHeight) / 42));
    }
  },
  watch: {
    scrollTop(newValue, oldValue) {},
    startIndex(newValue, oldValue) {
      // console.log(this.$refs.spacer.children);
    }
  },
  beforeUpdate() {
    // console.log('before update', this.$refs.spacer.children);
  },
  mounted() {
    this.isMounted = true;
    const children = this.$refs.spacer.children;
    for (let i = 0; i < children.length; i++) {
      // console.log(children[i].offsetTop - this.$el.offsetTop);
      children[i].setAttribute("data-height", children[i].scrollHeight);
    }

    bus.$on("incrementStart", () => {
      this.startIndex++;
    });
    bus.$on("decrementStart", () => {
      this.startIndex--;
    });
    bus.$on("incrementEnd", () => {
      this.endIndex++;
    });
    bus.$on("decrementEnd", () => {
      this.endIndex--;
    });
    this.observer = new MutationObserver(this.onMutation);
    this.observer.observe(this.$refs.spacer, {
      childList: true,
      attributes: true
    });
    this.$el.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    this.observer && this.observer.disconnect();
    this.$el.removeEventListener("scroll", this.handleScroll);
  }
});

new Vue({
  el: "#app",
  methods: {
    incrementStart() {
      bus.$emit("incrementStart");
    },
    decrementStart() {
      bus.$emit("decrementStart");
    },
    incrementEnd() {
      bus.$emit("incrementEnd");
    },
    decrementEnd() {
      bus.$emit("decrementEnd");
    }
  }
});
  • В приведенном выше методе onMutation вы можете видеть, что scrollHeight для удаленных узлов, offsetHeight, clientHeight et c всегда равен 0, как получить их значения?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...