div构建滚动条_构建滚动时会粘住的盒子

div构建滚动条

粘滞框是无论您在页面上的什么位置滚动浏览器都保持可见的框。 它们最常用于侧栏和标题栏中,以使品牌和导航菜单始终可见且可访问。 过去,粘性框非常基本,无论滚动到什么位置,粘性框都只能固定在视口的一部分上,如Yahoo! 主页。

yahoo_2001

而且它们也很容易用CSS来实现,如以下IE6解决方法所示。

<style>
  #header {
    position: fixed;
    top: 0px;
  }
  * html #header {
    position: absolute;
    top: expression(document.body.scrollTop);
  }
</style>

但是如今,网页已经发展起来,根据网页的滚动位置,便笺盒需要放置在不同的位置。 例如,请查看本文的演示页面, 世界各地的瑜伽店 。 注意徽标和语音气泡如何在主要内容旁边优雅地漂浮。 当您位于页面顶部时,便笺盒可以固定在屏幕中间。 向下滚动时,粘性框会优雅地向上滑动,然后在整个会话期间紧贴视口顶部。 然后,当您靠近页面底部(或边界)时,粘性框会进一步向上滑动,直到其消失。 只需几行代码即可获得非常流畅的体验。

计划

首先,我们需要一个在滚动页面时调用的函数。 此函数必须遍历页面上的每个粘性元素,以确定该元素是否为:

  1. 在视口顶部边缘下方。
  2. 在视口顶部边缘上方,以及
    • 不接触其边界的底部边缘。
    • 触摸其边界的底部边缘。

现在,让我们开始下面的框架代码:

document.onscroll = onScroll;

function onScroll() {
  var list = getAllStickies();

  for (var i = 0, item; item = list[i]; i++) {
    var bound = getBoundary(item);
    var edge = bound.getBoundingClientRect().bottom;
    var height = item.offsetHeight;
    var top = item.getBoundingClientRect().top;

    if (top < 0) {
      // above the top edge of the viewport
      if (edge > height) {
        // not touching the bottom edge of its boundary
        item.style.position = "fixed";
        item.style.top = "0px";
      } else {
        // touching the bottom edge of its boundary
        item.style.position = "relative";
        item.style.top = -((top - edge) + height) + "px";
      }
    } else {
      // below the top edge of the viewport
      item.style.position = "relative";
      item.style.top = "auto";
    }
  }
}

函数getAllStickies()getBoundary()尚未定义。 我们待会儿再讨论。 getBoundingClientRect()函数是一种方便快捷的函数,用于返回元素相对于视口的位置。 视口上方的元素为负数。 通过使用此功能,我们只需要检查最大值是正数还是负数。

我们的功能为每个粘性元素检测三种情况:

  1. 如果该元素在视口的上边缘下方,则该元素仍是页面的一部分,并且应处于其自然位置,以便它随页面滚动。
  2. 如果元素在视口的顶部边缘上方(即隐藏),并且未接触其边界的底部边缘,则应将元素移至视口的顶部,并将其position设置为fixed
  3. 如果元素在视口的顶部边缘上方(即隐藏),并且正在接触其边界的底部边缘,则应将元素移动到恰好在边界边缘的上方。 在这种情况下,其position设置为relative以便可以随页面滚动。

现在逻辑已经就绪,让我们讨论语义。

标记

我们将粘性元素定义为包含x-sticky属性的元素。 粘性是由x-sticky-boundary属性标识的边界元素的子代或后代。 胶粘物可以在边界元素的范围内自由移动。 粘性和边界示例如下所示。

<div x-sticky-boundary="">
  <div x-sticky="">I am a sticky confined within a boundary</div>
</div>

接下来,我们将实现我们前面提到的getAllStickies()getBoundary()函数。 我们可以将getAllStickies()替换为:

var list = document.querySelectorAll("[x-sticky]");

另外,我们可以实现getBoundary()以返回带有x-sticky-boundary属性的第一个祖先元素(或返回body元素):

function getBoundary(n) {
  while (n = n.parentNode) {
    if (n.hasAttribute("x-sticky-boundary")) {
      return n;
    }
  }

  return document.body || document.documentElement;
}

当前,该代码每个边界仅支持一个粘性。 但是,通常情况下,我们每个边界有两个或多个不应相互冲突的胶粘物。 如果第二个便利贴移动到视口的顶部,则应将第一个便利贴推开。

以前,我们假设边界的底边是边界极限。 我们需要对其进行修改,以检查同一边界内下一个粘性元素的上边缘。

var edge = bound.getBoundingClientRect().bottom;
var nextItem = findNextInBoundary(list, i, bound);

if (nextItem) {
  edge = nextItem.getBoundingClientRect().top;
}

我们定义了一个新函数findNextInBoundary() ,该函数从定义的索引开始遍历数组,查找与当前粘滞共享边界元素的下一个粘滞。

下降

到目前为止,我们还没有考虑过一种主要情况。 页面滚动后,我们已将粘性元素动态移动到页面上的另一个位置。 这意味着未保留粘性元素的原始位置,这意味着当用户向上滚动时我们无法恢复其原始位置。

同样,当将粘滞物放入固定位置元素时,它会从文档流中拉出,这意味着其下方的内容将向上移动。 我们想要保留它所占据的空间,以使它下面的内容不会跳来跳去。 要解决此问题,我们需要在占位符的原始位置放置一个占位符元素。 我们还将粘性放置在占位符内,以免影响占位符兄弟姐妹的nth-child伪选择器。 然后,每当我们需要恢复便签的位置时,便用便签替换占位符,并丢弃占位符。

要记住的一件事是,如果我们要获取即时贴的初始位置,则应该获取其占位符的当前位置。 这是我们的更新功能:

document.onscroll = onScroll;

function onScroll() {
  var list = document.querySelectorAll("[x-sticky]");

  for (var i = 0, item; item = list[i]; i++) {
    var bound = getBoundary(item);
    var edge = bound.getBoundingClientRect().bottom;
    var nextItem = findNextInBoundary(list, i, bound);

    if (nextItem) {
      if(nextItem.parentNode.hasAttribute("x-sticky-placeholder")) {
        nextItem = nextItem.parentNode;
      }

      edge = nextItem.getBoundingClientRect().top;
    }

    // check if the current sticky is already inside a placeholder
    var hasHolder = item.parentNode.hasAttribute("x-sticky-placeholder");
    var rect = item.getBoundingClientRect();
    var height = rect.bottom - rect.top; // get the height and width
    var width = rect.right - rect.left;
    var top = hasHolder ? item.parentNode.getBoundingClientRect().top : rect.top;

    if (top < 0) {
      if(edge > height) {
        item.style.position = "fixed";
        item.style.top = "0px";
      } else {
        item.style.position = "relative";
        item.style.top = -((top - edge) + height) + "px";
      }

      if (!hasHolder) {  //create the placeholder
        var d = document.createElement("div");

        d.setAttribute("x-sticky-placeholder", "");
        d.style.height = height + "px";  //set the height and width
        d.style.width = width + "px";
        item.parentNode.insertBefore(d, item);
        d.appendChild(item);
      }
    } else {
      item.style.position = "relative";
      item.style.top = "auto";

      if (hasHolder) {  //remove the placeholder
        item = item.parentNode;
        item.parentNode.insertBefore(item.firstChild, item);
        item.parentNode.removeChild(item);
      }
    }
  }
}

function findNextInBoundary(arr, i, boundary) {
  i++;

  for (var item; item = arr[i]; i++) {
    if (getBoundary(item) == boundary) {
      return item;
    }
  }
}

function getBoundary(n) {
  while (n = n.parentNode) {
    if (n.hasAttribute("x-sticky-boundary")) {
      return n;
    }
  }

  return document.body || document.documentElement;
}

诱饵

为了最大限度地利用占位符,我们还需要将几个CSS属性从sticky元素复制到占位符。 例如,我们希望边距相同,以使其占用完全相同的空间量。 我们还希望保留float属性,以免混淆基于浮动的网格布局。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

让我们介绍一个函数copyLayoutStyles() ,该函数在创建占位符后立即调用以将样式复制到占位符:

function copyLayoutStyles(to, from) {
  var props = {
    marginTop: 1,
    marginRight: 1,
    marginBottom: 1,
    marginLeft: 1
  };

  if (from.currentStyle) {
    props.styleFloat = 1;

    for (var s in props) {
      to.style[s] = from.currentStyle[s];
    }
  } else {
    props.cssFloat = 1;

    for (var s in props) {
      to.style[s] = getComputedStyle(from, null)[s];
    }
  }
}

清理

目前,我们正在将元素的position属性直接设置为fixedrelative 。 让我们将该调用移到CSS样式表中,并使用选择器应用属性。 这允许其他程序员在必要时覆盖默认行为。 CSS样式表将如下所示:

<style>
  [x-sticky] {margin:0}
  [x-sticky-placeholder] {padding:0; margin:0; border:0}
  [x-sticky-placeholder] > [x-sticky] {position:relative; margin:0 !important}
  [x-sticky-placeholder] > [x-sticky-active] {position:fixed}
</style>

与其创建单独的样式表,不如通过创建临时元素并使用样式表将其设置为innerHTML来使用JavaScript注入此样式表。 然后,我们可以将结果附加到文档中,如下所示。

var css = document.createElement("div");
css.innerHTML = ".<style>" + 
  "[x-sticky] {margin:0}" +
  "[x-sticky-placeholder] {padding:0; margin:0; border:0}" +
  "[x-sticky-placeholder] > [x-sticky] {position:relative; margin:0 !important}" +
  "[x-sticky-placeholder] > [x-sticky-active] {position:fixed}" +
  "<\/style>";
var s = document.querySelector("script");
s.parentNode.insertBefore(css.childNodes[1], s);

在main函数内部,我们需要用item.setAttribute("x-sticky-active", "")替换每次出现的item.style.position = "fixed" ,以便CSS选择器可以匹配该属性。 为了使该代码可移植,我们还需要将所有内容包装在一个闭包中,以使私有变量保持私有。 我们还需要使用addEventListener()而不是分配给document.onscroll以避免可能的冲突。 并且,在进行此操作时,让我们添加一个API检查(如下所示),以便我们的功能不会在较旧的浏览器中运行。

if (document.querySelectorAll && 
    document.createElement("b").getBoundingClientRect)
(function(doc) {
"use strict";

init();

function init() {
  if(window.addEventListener) {
    addEventListener("scroll", onScroll, false);
  } else {
    attachEvent("onscroll", onScroll);
  }

  var css = doc.createElement("div");

  css.innerHTML = ".<style>" + 
    "[x-sticky] {margin:0}" +
    "[x-sticky-placeholder] {padding:0; margin:0; border:0}" +
    "[x-sticky-placeholder] > [x-sticky] {position:relative; margin:0!important}" +
    "[x-sticky-placeholder] > [x-sticky-active] {position:fixed}<\/style>";

  var s = doc.querySelector("script");
  s.parentNode.insertBefore(css.childNodes[1], s);
}

function onScroll() {
  var list = doc.querySelectorAll("[x-sticky]");

  for (var i = 0, item; item = list[i]; i++) {
    var bound = getBoundary(item);
    var edge = bound.getBoundingClientRect().bottom;
    var nextItem = findNextInBoundary(list, i, bound);

    if (nextItem) {
      if (nextItem.parentNode.hasAttribute("x-sticky-placeholder")) {
        nextItem = nextItem.parentNode;
      }

      edge = nextItem.getBoundingClientRect().top;
    }

    var hasHolder = item.parentNode.hasAttribute("x-sticky-placeholder");
    var rect = item.getBoundingClientRect();
    var height = rect.bottom - rect.top;
    var width = rect.right - rect.left;
    var top = hasHolder ? item.parentNode.getBoundingClientRect().top : rect.top;

    if (top < 0) {
      if (edge > height) {
        if (!item.hasAttribute("x-sticky-active")) {
          item.setAttribute("x-sticky-active", "");
        }

        item.style.top = "0px";
      } else {
        if (item.hasAttribute("x-sticky-active")) {
          item.removeAttribute("x-sticky-active");
        }

        item.style.top = -((top - edge) + height) + "px";
      }

      if (!hasHolder) {
        var d = doc.createElement("div");

        d.setAttribute("x-sticky-placeholder", "");
        d.style.height = height + "px";
        d.style.width = width + "px";
        copyLayoutStyles(d, item);
        item.parentNode.insertBefore(d, item);
        d.appendChild(item);
      }
    } else {
      if (item.hasAttribute("x-sticky-active")) {
        item.removeAttribute("x-sticky-active");
      }

      item.style.top = "auto";

      if(hasHolder) {
        item = item.parentNode;
        item.parentNode.insertBefore(item.firstChild, item);
        item.parentNode.removeChild(item);
      }
    }
  }
}

function findNextInBoundary(arr, i, boundary) {
  i++;

  for (var item; item = arr[i]; i++) {
    if (getBoundary(item) == boundary) {
      return item;
    }
  }
}

function getBoundary(n) {
  while (n = n.parentNode) {
    if (n.hasAttribute("x-sticky-boundary")) {
      return n;
    }
  }

  return doc.body || doc.documentElement;
}

function copyLayoutStyles(to, from) {
  var props = {
    marginTop: 1,
    marginRight: 1,
    marginBottom: 1,
    marginLeft: 1
  };

  if (from.currentStyle) {
    props.styleFloat = 1;

    for (var s in props) {
      to.style[s] = from.currentStyle[s];
    }
  } else {
    props.cssFloat = 1;

    for (var s in props) {
      to.style[s] = getComputedStyle(from, null)[s];
    }
  }
}
})(document);

结论

在那里,您拥有了! 通过使用x-sticky属性标记元素,元素将随着页面滚动直到到达顶部,并且会一直徘徊直到遇到边界边缘,然后边界消失在页面上。

翻译自: https://www.sitepoint.com/building-box-sticks-scroll/

div构建滚动条

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值