div构建滚动条
粘滞框是无论您在页面上的什么位置滚动浏览器都保持可见的框。 它们最常用于侧栏和标题栏中,以使品牌和导航菜单始终可见且可访问。 过去,粘性框非常基本,无论滚动到什么位置,粘性框都只能固定在视口的一部分上,如Yahoo! 主页。
而且它们也很容易用CSS来实现,如以下IE6解决方法所示。
<style>
#header {
position: fixed;
top: 0px;
}
* html #header {
position: absolute;
top: expression(document.body.scrollTop);
}
</style>
但是如今,网页已经发展起来,根据网页的滚动位置,便笺盒需要放置在不同的位置。 例如,请查看本文的演示页面, 世界各地的瑜伽店 。 注意徽标和语音气泡如何在主要内容旁边优雅地漂浮。 当您位于页面顶部时,便笺盒可以固定在屏幕中间。 向下滚动时,粘性框会优雅地向上滑动,然后在整个会话期间紧贴视口顶部。 然后,当您靠近页面底部(或边界)时,粘性框会进一步向上滑动,直到其消失。 只需几行代码即可获得非常流畅的体验。
计划
首先,我们需要一个在滚动页面时调用的函数。 此函数必须遍历页面上的每个粘性元素,以确定该元素是否为:
- 在视口顶部边缘下方。
- 在视口顶部边缘上方,以及
- 不接触其边界的底部边缘。
- 触摸其边界的底部边缘。
现在,让我们开始下面的框架代码:
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()
函数是一种方便快捷的函数,用于返回元素相对于视口的位置。 视口上方的元素为负数。 通过使用此功能,我们只需要检查最大值是正数还是负数。
我们的功能为每个粘性元素检测三种情况:
- 如果该元素在视口的上边缘下方,则该元素仍是页面的一部分,并且应处于其自然位置,以便它随页面滚动。
- 如果元素在视口的顶部边缘上方(即隐藏),并且未接触其边界的底部边缘,则应将元素移至视口的顶部,并将其
position
设置为fixed
。 - 如果元素在视口的顶部边缘上方(即隐藏),并且正在接触其边界的底部边缘,则应将元素移动到恰好在边界边缘的上方。 在这种情况下,其
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
属性直接设置为fixed
或relative
。 让我们将该调用移到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
属性标记元素,元素将随着页面滚动直到到达顶部,并且会一直徘徊直到遇到边界边缘,然后边界消失在页面上。
div构建滚动条