【问题标题】:Using getBoundingClientRect() when resizing the window调整窗口大小时使用 getBoundingClientRect()
【发布时间】:2021-09-15 21:07:18
【问题描述】:

我有这个导航栏,每次单击导航栏中的选项时,绝对定位指示器都会在 getBoundingClientRect() 的帮助下获取左侧选项的位置和宽度,并将其移动到目标。

问题是当我调整窗口大小时,指示器会改变它的位置并移开。为了在调整窗口大小时保持在同一个位置,我将 eventListener 应用于窗口,每次调整大小时,我都会得到 @987654322 的新值@ 和 widthgetBoundingClientRect()

它有效,但我想知道这是否是一种不好的方法,因为每次调整窗口大小时都会发生计算,如果是这种情况,有什么更好的方法。

代码如下:

const navigator = document.querySelector('.navigator');
const firstOption = document.querySelector('.first-option');
const navOptions = document.querySelectorAll('.nav-option');
const nav = document.querySelector('nav');

navigator.style.left = `${firstOption.getBoundingClientRect().left}px`;
navigator.style.width = `${firstOption.getBoundingClientRect().width}px`;

nav.addEventListener('click', function(e) {
  if(e.target.classList.contains('nav-option')) {
    navOptions.forEach(option => option.classList.remove('nav-option-active'));
    e.target.classList.add('nav-option-active');

    navigator.style.left = `${e.target.getBoundingClientRect().left}px`;
    navigator.style.width = `${e.target.getBoundingClientRect().width}px`;
  };
});

window.addEventListener('resize', function() {
  let navOptionActive = nav.querySelector('.nav-option-active');

  navigator.style.left = `${navOptionActive.getBoundingClientRect().left}px`;
  navigator.style.width = `${navOptionActive.getBoundingClientRect().width}px`;
});
* {
  margin: 0;
  padding: 0;
}

nav {
 position: relative;
 display: flex;
 justify-content: space-between;
 align-items: center;
 margin: 100px auto;
 padding: 7vh 30vw;
 width: auto;
 background:#eeeeee;
}

.nav-option {
  padding: 0 15px;
  font-size: 22px;
  cursor: pointer;
}

.navigator {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 5px;
  background: orangered;
  transition: .4s ease all;
}

@media (max-width: 1200px) {
  .nav-option {
    font-size: 18px;
    padding: 10px;
  }
}
<nav>
  <div class="navigator"></div>
  <div class="nav-option first-option nav-option-active">HOME</div>
  <div class="nav-option">INFO</div>
  <div class="nav-option">CONTACT</div>
  <div class="nav-option">ABOUT</div>
  <div class="nav-option">MENU</div>
</nav>

【问题讨论】:

  • 如果您不想在脚本中完成所有这些工作(计算),您可以考虑在每个nav-option 下添加一个navigator,并根据活动@987654330 选择性地显示/隐藏它们@.

标签: javascript html css


【解决方案1】:

您可以使您的&lt;nav&gt; 元素紧紧包裹按钮,然后将下划线相对于&lt;nav&gt; 定位。 &lt;nav&gt; 周围的新包装器 &lt;div&gt; 负责处理边距和灰色背景。然后你需要使用offsetLeftoffsetWidth 而不是getBoundingClientRect()

请注意,这不会处理响应您的 @media 查询的更改。为此,您可以添加一个 resize 侦听器,专门仅处理 1200 像素阈值上的更改。或者,您可以在没有动画时将下划线重新设置为实际导航按钮的子项。两种解决方案都不是很好,但都可以完成工作。

const navigator = document.querySelector('.navigator');
const firstOption = document.querySelector('.first-option');
const navOptions = document.querySelectorAll('.nav-option');
const nav = document.querySelector('nav');

navigator.style.left = `${firstOption.offsetLeft}px`;
navigator.style.width = `${firstOption.offsetWidth}px`;

nav.addEventListener('click', function(e) {
  if(e.target.classList.contains('nav-option')) {
    navOptions.forEach(option => option.classList.remove('nav-option-active'));
    e.target.classList.add('nav-option-active');

    navigator.style.left = `${e.target.offsetLeft}px`;
    navigator.style.width = `${e.target.offsetWidth}px`;
  };
});
* {
  margin: 0;
  padding: 0;
}

.nav-wrapper {
  margin: 100px 0;
  display: flex;
  justify-content: center;
  background: #eeeeee;
}

nav {
  position: relative;
  display: flex;
}

.nav-option {
  padding: 7vh 15px;
  font-size: 22px;
  cursor: pointer;
}

.navigator {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 5px;
  background: orangered;
  transition: .4s ease all;
}

@media (max-width: 1200px) {
  .nav-option {
    font-size: 18px;
    padding: 10px;
  }
}
<div class="nav-wrapper">
  <nav>
    <div class="navigator"></div>
    <div class="nav-option first-option nav-option-active">HOME</div>
    <div class="nav-option">INFO</div>
    <div class="nav-option">CONTACT</div>
    <div class="nav-option">ABOUT</div>
    <div class="nav-option">MENU</div>
  </nav>
</div>

【讨论】:

    【解决方案2】:

    如果你必须使用getBoundingClientRect(老实说它没有任何问题),你可以限制调用,这样只有经过足够时间后的最后一次调整大小才会执行。有无数种方法可以做到这一点,我将举一个例子:

    window.onresize = (function(id = null, delay = 600, oEvent = null){
        return function fire(event){
            return (new Promise(function(res,rej){
                if (id !== null){
                    oEvent = event;
                    rej("busy");
                    return;
                }
                id = setTimeout(function(){
                    res(oEvent || event);
                },delay);
            })).then(function(event){
                id = null;
                console.log(event, "do getBoundingClientRect call");
            }).catch(function(){void(0);});
        };
    }());
    

    console.log 替换为您要执行的操作。

    如果您可以重构渲染逻辑,您的另一个选择是切换到intersection observer。这将需要一些工作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-15
      • 2011-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-20
      • 1970-01-01
      • 2012-08-02
      相关资源
      最近更新 更多