【问题标题】:Prevent page jump on Button click? ReactJS防止按钮点击页面跳转?反应JS
【发布时间】:2020-03-25 09:39:09
【问题描述】:

我有一个 ShowcaseMovie 组件,它获取componentDidMount() 上的数据并将其设置为状态。该组件呈现卡片组件以显示数据以及四个按钮元素upcomingtop_ratedpopularnow_playing,允许用户在相关数据之间切换。每个按钮都有一个 onClick 事件,该事件调用 changeFilter 并将状态 currentFilter 设置为所选键。

问题:当点击过滤器按钮时,有时页面会跳到顶部(如果它还没有)。我试图找到解决方案,但我似乎无法理解发生了什么。任何建议都会有很大帮助,在此先感谢您。

更新:当没有为具有动态子元素的元素设置高度时,似乎会发生此问题。如果我将 ShowcaseMovie 上的高度设置为 height: 200vh 这样的大值,它就会消失。

我相信我已经解决了我的问题,但我很想听听其他关于为什么会发生这种情况以及其他一些解决方法的想法。当您不知道要渲染多少内容(或该内容的高度)时,很难为父级设置高度。 min-height 会有所帮助,但仍然可以快速解决。

ShowcaseMovie.js

import React, { Component } from "react";
import Card from "./Card";
import "../css/ShowcaseMovie.css";
import { v4 as uuidv4 } from "uuid";
import { formatString, buildMovieState } from "../utilities";

class ShowcaseMovie extends Component {
  static defaultProps = {
    filterNames: ["upcoming", "popular", "now_playing", "top_rated"]
  };

  constructor(props) {
    super(props);
    this.state = {
      upcoming: [],
      now_playing: [],
      popular: [],
      top_rated: [],
      currentFilter: this.props.filterNames[0]
    };
  }

  changeFilter = e => {
    e.preventDefault();
    const type = e.target.name;
    this.setState({ currentFilter: type });
  };

  componentDidMount() {
    this.props.filterNames.map(name => this.fetchMovies(name));
    // setInterval(() => {
    //   this.timeoutFilter();
    // }, 10000);
  }

  async fetchMovies(type) {
    try {
      const res = await fetch(
        `url`
      );
      const data = await res.json();
      if (data) {
        this.setState(state => ({
          ...state,
          [type]: buildMovieState(data)
        }));
      }
    } catch (error) {
      console.log(error);
    }
  }

  render() {
    const { currentFilter } = this.state;
    const movies = this.state[currentFilter].map((movie, i) => (
      <Card key={uuidv4()} movie={movie} index={i} />
    ));
    const buttons = this.props.filterNames.map(name => (
      <button
        type="button"
        key={name}
        name={name}
        className={`ShowcaseMovie-btn ${
          currentFilter === name ? "active" : ""
        }`}
        disabled={currentFilter === name}
        onClick={this.changeFilter}>
        {formatString(name)}
      </button>
    ));

    return (
      <section className="ShowcaseMovie">
        <div className="ShowcaseMovie-container">
          <h2 className="ShowcaseMovie-header">Movies</h2>
          <div className="ShowcaseMovie-btn-container">{buttons}</div>
        </div>
        <div className="ShowcaseMovie-grid">{movies}</div>
      </section>
    );
  }
}

ShowcaseMovie.css

.ShowcaseMovie {
  padding: 4rem 10%;
}

.ShowcaseMovie-container {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2rem;
}

.ShowcaseMovie-button-container {
  display: flex;
  justify-content: center;
  align-items: center;
}

.ShowcaseMovie-container::after {
  content: "";
  background-color: #64b5f6;
  height: 80%;
  width: 6px;
  left: 0;
  position: absolute;
  border-radius: 1px;
}

.ShowcaseMovie-header {
  font-size: 3rem;
  font-weight: 200;
  margin: 0 5rem;
}

.ShowcaseMovie-btn {
  outline: none;
  border: none;
  background-color: transparent;
  font-size: 1.6rem;
  font-weight: 500;
  letter-spacing: 1px;
  padding: 1rem;
  margin-left: 4rem;
  color: white;
  opacity: 0.5;
  cursor: pointer;
  transition-property: opacity;
  transition-duration: 300ms;
  transition-timing-function: ease;
}

.ShowcaseMovie-btn:hover {
  opacity: 1;
  transition-property: opacity;
  transition-duration: 300ms;
  transition-timing-function: ease;
}

.ShowcaseMovie-btn.active {
  opacity: 1;
  cursor: auto;
  color: #64b5f6;
}

.ShowcaseMovie-grid {
  display: grid;
  gap: 3rem;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}

Card.js

import React, { Component } from "react";
import "../css/Card.css";

class Card extends Component {
  render() {
    const { title, poster_path } = this.props.movie;
    const style = { animationDelay: `${80 * this.props.index}ms` };
    return (
      <div className="Card">
        <div className="Card-inner" style={style}>
          <img
            src={`https://image.tmdb.org/t/p/w500/${poster_path}`}
            alt=""
            className="Card-img"
          />
          <p className="Card-name">{title}</p>
        </div>
      </div>
    );
  }
}

export default Card;

Card.css

.Card {
  display: block;
  transition: transform 300ms ease;
}

.Card:hover {
  transform: translateY(-5px);
  transition: transform 300ms ease;
}

.Card-inner {
  position: relative;
  display: block;
  cursor: pointer;
  height: 100%;
  opacity: 0;
  animation-name: moveUp;
  animation-duration: 500ms;
  animation-delay: 50ms;
  animation-timing-function: ease;
  animation-fill-mode: forwards;
}

.Card-inner::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: linear-gradient(transparent, rgba(33, 47, 61, 0.8));
  border-bottom-left-radius: 2px;
  border-bottom-right-radius: 2px;
  z-index: 100;
  opacity: 1;
  transition: opacity 300ms ease;
}

.Card-inner:hover::after {
  opacity: 0;
  transition: opacity 300ms ease;
}

.Card-inner::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: linear-gradient(transparent, rgba(100, 180, 246, 0.6));
  border-bottom-left-radius: 2px;
  border-bottom-right-radius: 2px;
  z-index: 100;
  opacity: 0;
  transition: opacity 300ms ease;
}

.Card-inner:hover::before {
  opacity: 1;
  transition: opacity 300ms ease;
}

.Card-img {
  display: block;
  position: relative;
  object-fit: cover;
  max-width: 100%;
  min-height: 100%;
  z-index: 0;
  border-radius: 2px;
}

.Card-name {
  position: absolute;
  bottom: 0;
  left: 0;
  margin: 0 2rem 2rem 2rem;
  z-index: 150;
  font-weight: 400;
  text-transform: uppercase;
  font-size: 1.4rem;
  letter-spacing: 2px;
}

@keyframes moveUp {
  0% {
    transform: translateY(5rem);
  }

  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

utilities.js

export const formatString = name => {
  return name
    .replace("_", " ")
    .split(" ")
    .map(w => w[0].toUpperCase() + w.slice(1))
    .join(" ");
};

export const buildMovieState = data => {
  if (data.results) {
    const movies = data.results.filter(
      d => d.backdrop_path && d.id && d.title && d.poster_path
    );
    return movies.length > 10 ? movies.slice(0, 10) : movies;
  } else {
    return [];
  }
};

【问题讨论】:

  • 请同时包含卡片组件源代码。
  • 嗨 Leon,我刚刚添加了 Card 组件源。谢谢
  • 对于这种有 UI 问题的情况,离开 CodeSandBox 项目是一个很好的解决方案。包括代码只会使解决方案比以前更难。
  • 我尝试创建一个使用您的代码的代码框。我添加了一些虚假数据,并且在单击按钮时确实看到了“跳转”页面。但这就是浏览器的反应方式:只有当新视图的高度小于前一个视图并且保持相同的滚动是不可能的时才会发生,因为页面的“深度”不同。这是你说的问题吗? codesandbox.io/s/affectionate-zhukovsky-fonbc
  • 我刚刚创建了一个 CodeSandbox,然后我在页面顶部添加了一个分隔符来解决这个问题。如果您向下滚动到 ShowcaseMovie 组件(因此空白不显示),然后在过滤器按钮之间单击,您会发现它将跳转到页面顶部。每点击 10 次左右就会发生这种情况。

标签: css reactjs onclick page-jump


【解决方案1】:

我可以通过从onClick 调用返回false 来阻止这种情况的发生。像这样:

onClick={
   doSomething()
   return false
}

【讨论】:

【解决方案2】:

页面跳转可能来自不必要地重新渲染的组件。尝试使用 React.memo( comonent name) 包装所有组件

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-10
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 2016-08-14
    • 2023-02-21
    • 1970-01-01
    相关资源
    最近更新 更多