javascript 等效于 React、onScroll 中的 document.querySelectorAll()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49344348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 08:32:05  来源:igfitidea点击:

Equivalent of document.querySelectorAll() in React, onScroll

javascriptreactjs

提问by Giovanni

What's the equivalent of document.querySelectorAll('.classname')in React? I understand I should use Refs, but how dow I observe multiple Refs onScroll?

document.querySelectorAll('.classname')React 中的等价物是什么?我知道我应该使用 Refs,但是我如何观察多个 RefsonScroll呢?

I usually use a function like the one below to check the viewport position of multiple elements in the page, and trigger different css animation when each element enters the viewport:

我通常使用类似下面这样的函数来检查页面中多个元素的视口位置,并在每个元素进入视口时触发不同的css动画:

HTML

HTML

<ul>
  <li data-position="below-viewport"></li>
  <li data-position="below-viewport"></li>
  <li data-position="below-viewport"></li>
  <li data-position="below-viewport"></li>
</ul>

Javascript

Javascript

getPosition: function (element) {
  const rect = element.getBoundingClientRect();

  if ((rect.top > -1) && (rect.top < (window.innerHeight * 0.75))) {
    element.setAttribute('data-js-position','in-viewport');
  } else if ((rect.top > 0) && (rect.top < window.innerHeight)) {
    element.setAttribute('data-js-position','entering-viewport');
  } else if (rect.top > window.innerHeight) {
    element.setAttribute('data-js-position','below-viewport');
  } else if (rect.bottom < 0) {
    element.setAttribute('data-js-position','above-viewport');
  }
}

window.addEventListener('scroll', function() {
  [].forEach.call(document.querySelectorAll('[data-js-position]'), el => {
    positionChecker.getPosition(el);
  })
});

How would I implement something similar in React? Can you give me an example of a function that observes multiple divs in React?

我将如何在 React 中实现类似的东西?你能举一个在 React 中观察多个 div 的函数的例子吗?

Even better: how can I abstract this function in App.js, so that I can use it also in child Components?

更好的是:我怎样才能在 中抽象这个函数App.js,以便我也可以在子组件中使用它?

回答by Andrew

Make each lihtml element its own component and hold a refreference to it in its own state.

使每个lihtml 元素成为自己的组件,并ref在其自己的状态下保存对它的引用。

class LiElement extends React.Component {
  componentDidMount() {
    this.ref.getPosition()
  }

  render() {
    return (
      <li ref={(ctx) => this.ref = ctx}>
      </li>
    )
  }
}

回答by Ignacio

Iterating through refs might get you something similar to what you're trying to achieve.

遍历 refs 可能会得到类似于你想要实现的东西。

In this example I'm storing every node in a local Map so you can iterate through them and get their getBoundingClientRect.

在本例中,我将每个节点存储在本地 Map 中,以便您可以遍历它们并获取它们的 getBoundingClientRect。

Note, that there are several ways of doing this, you don't have to create a Map, you can just get each element's "ref", but you would have to assign a different "ref" value to each "li".

请注意,有几种方法可以做到这一点,您不必创建 Map,您只需获取每个元素的“ref”,但您必须为每个“li”分配不同的“ref”值。

Also, it would be a good idea to throttle the handleScroll call..

此外,最好限制 handleScroll 调用。.

class App extends React.Component {
  constructor(props) {
    super(props);
    this._nodes = new Map();
  }

  componentDidMount() {
    window.addEventListener("scroll", this.handleScroll);
  }

  handleScroll = e => {
    Array.from(this._nodes.values())
      // make sure it exists
      .filter(node => node != null)
      .forEach(node => {
        this.getPosition(node);
      });
  };

  getPosition = node => {
    const rect = node.getBoundingClientRect();
    // do something cool here
    console.log("rect:", rect);
  };

  componentWillUnmount() {
    window.removeEventListener("scroll", this.handleScroll);
  }

  render() {
    return (
      <div>
        <ul>
          <li key="1" ref={c => this._nodes.set(1, c)}>
            Your thing 1
          </li>
          <li key="2" ref={c => this._nodes.set(2, c)}>
            Your thing 2
          </li>
          <li key="3" ref={c => this._nodes.set(3, c)}>
            Your thing 3
          </li>
        </ul>
      </div>
    );
  }
}

回答by Giovanni

Adding this other possible solution: creating a new react component VisibilitySensor, so to use only one ref and only one function

添加另一种可能的解决方案:创建一个新的反应组件VisibilitySensor,因此只使用一个引用和一个函数

import React, { Component } from 'react';

class VisibilitySensor extends Component {

  componentDidMount() {
    window.addEventListener('scroll', this.getElementPosition = this.getElementPosition.bind(this));
    this.getElementPosition();
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.getElementPosition);
  }

  getElementPosition() {
    const element = this.visibilitySensor;
    var rect = element.getBoundingClientRect();

    if ((rect.top > 0) && (rect.top < (window.innerHeight * 0.75))) {
      element.setAttribute("data-position","in-viewport");
    } else if (rect.top > window.innerHeight) {
      element.setAttribute("data-position","below-viewport");
    } else if (rect.bottom < 0) {
      element.setAttribute("data-position","above-viewport");
    }
  }

  render() {
    return (
      <div data-position="below-viewport" ref={(element) => { this.visibilitySensor = element; }}>
        {this.props.children}
      </div>
    );
  }
}

export default VisibilitySensor;

Then wrapping every li(or div) I need to watch with the above component. I personally ended up disliking the solution above, because the new added divwould mess up with my css styling, particularly width of child related to width of parent, etc..

然后用上面的组件包装我需要观看的每个li(或div)。我个人最终不喜欢上面的解决方案,因为新添加的内容div会弄乱我的 css 样式,尤其是与父项宽度相关的子项宽度等。

 <ul>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
  <VisibilitySensor>
    <li></li>
  </VisibilitySensor>
</ul>