quiz
quiz copied to clipboard
DOM基础测试27
题目如下,入门级难度:
使用原生JS代码实现。
大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。
```js // 你的JS代码写在这里 ```
第二小题细节补充:
网页默认固定是HTML或者body,但有时候也可能是DIV,因为布局需要。和题1唯一不同就是容器变了。不要想太多。
第一题:(Throttle是一个节流函数)
(function() {
let backTop = document.getElementById('backTop'),
scrollTop = 0,
windowHeight = window.innerHeight || document.documentElement.clientHeight;
let scroll = Throttle((e) => {
scrollTop = document.documentElement.scrollTop || window.pageYOffset ||
document.body.scrollTop;
if (scrollTop >= windowHeight) {
backTop.classList.add('show');
} else {
backTop.classList.remove('show');
}
}, 300)
document.addEventListener('scroll', scroll);
})();
第二题:(content是高度小于屏幕高度的元素overflow:hidden; wrapper是content的子元素overflow:auto;)
(function() {
let backTop = document.getElementById('backTop'),
content = document.getElementById('content'),
wrapper = document.getElementById('wrapper'),
scrollTop = 0,
windowHeight = content.innerHeight || content.clientHeight;
let scroll = Throttle((e) => {
scrollTop = wrapper.scrollTop;
if (scrollTop >= windowHeight) {
backTop.classList.add('show');
} else {
backTop.classList.remove('show');
}
}, 300);
wrapper.addEventListener('scroll', scroll);
})();
Throttle源码:
function Throttle(fn, delay) {
let last = 0,
timer = null;
return function() {
let context = this,
args = arguments,
now = +new Date();
if (now - last < delay) {
clearTimeout(timer)
timer = setTimeout(function() {
last = now;
fn.apply(context, args);
}, delay)
} else {
last = now;
fn.apply(context, args);
}
}
}
一个通吃两道小题的方案
JavaScript(ES6,需兼容自行 babel)
{
const
$back_to_top = document.getElementById('backTop'),
$parent = $back_to_top.parentElement;
const
getScrollTop = $parent === document.body ?
() => window.scrollY :
() => $parent.scrollTop,
getViewportHeight = $parent === document.body ?
() => window.innerHeight :
() => $parent.offsetHeight,
scrollBackToTop = $parent === document.body ?
() => window.scrollY = 0 :
() => $parent.scrollTop = 0;
$back_to_top.addEventListener('click', scrollBackToTop);
const wheelEventHandler = () => $back_to_top.classList[
getScrollTop() > getViewportHeight() ? 'remove' : 'add'
]('hidden');
// TODO: 节流函数,减少事件触发次数
// 要用的话直接 underscore 或者用原生的 setInterval 实现一个就行
const throttle = (fn, interval) => fn;
$parent.addEventListener('wheel', throttle(wheelEventHandler, 100));
$parent.addEventListener('scroll', throttle(wheelEventHandler, 100));
document.addEventListener('DOMContentLoaded', wheelEventHandler);
}
CSS(也可以不用,只是为了方便用 class 管理样式)
#backTop {
display: block;
position: fixed;
top: 0;
/* 自定义样式 */
}
#backTop.hidden {
visibility: hidden;
}
css
.hidden{
display: none;
}
#container{
overflow:auto;
height: 300px;
}
#backTop{
/* 若容器是html的时候#backTop是fixed定位 */
/* position: fixed; */
/* 若容器是div的时候#backTop是absolute定位 */
position: absolute;
right:30px;
margin-top:200px;
}
/* 辅助元素 */
.auxiliary{
height: 800px;
}
html
<!-- 当容器是html的时候 -->
<!-- <a
class="hidden"
id="backTop"
href="#"
>
返回顶部
</a>
<div
class="auxiliary"
>
</div> -->
<!-- 当容器是div的时候 -->
<div
id="container"
>
<a
class="hidden"
id="backTop"
href="#"
>
返回顶部
</a>
<div
class="auxiliary"
>
</div>
</div>
js
/* 滚动元素是window时 */
/* toggleBackTop(window,document.querySelector("#backTop")); */
/* 滚动元素是div时 */
toggleBackTop(document.querySelector("#container"),document.querySelector("#backTop"));
function toggleBackTop(container,backTopDom){
container.addEventListener("scroll",function(){
let containerHeight,
scrollHeight;
if(this instanceof Window){
containerHeight =
document.documentElement.clientHeight
||
document.body.clientHeight;
}else{
containerHeight = this.clientHeight;
}
scrollHeight = this.scrollY||this.scrollTop||0;
backTopDom.classList.toggle("hidden",scrollHeight<containerHeight);
})
}
借用一楼的Throttle
节流函数。
(function (doc, con) {
var backTop = doc.getElementById('backTop');
var body = con || doc.documentElement;
var setVisible = Throttle(function () {
var h = body.clientHeight;
var scrollTop = body.scrollTop;
if (scrollTop >= h) {
backTop.removeAttribute('hidden');
} else {
backTop.setAttribute('hidden', true)
}
}, 300)
var content = con||doc;
content.addEventListener('scroll', setVisible, false);
window.addEventListener('resize', setVisible, false);//改变窗口大小也需要监听
})(document)
如果滚动容器是div
var div = document.getElementById('wrap');
(function (doc, con) {
//...同上
})(document,div)
对于第一题,分析提议需要注意以下几点:
-
scroll
event作为高触发事件,回调函数里面进行高损耗性能操作我们需要throttle,而throttle无外乎:requestAnimationFrame()
,setTimeout()
或者CustomEvent
- 兼容PC端和移动端,使用
window.pageYOffset
而不是window.scrollY
(处于兼容性考虑,其实目前应该所有主流浏览器都支持这两个属性对)。另外我们需要修改Mobile浏览器的默认layout viewport等于ideal viewport的大小(<meta name="viewport" content="width=device-width,initial-scale=1.0">
) - 获取viewport高度时,我采用了
document.documentElement.clientHeight
,而没有采用window.innerHeight
,是因为我不希望加上可能出现的x轴scrollbar的高度 - 是否考虑浏览器zoom in/out 操作,因为zoom操作很显然会改变浏览器的visual viewport dimension.理论上来讲,当用户scroll up之后再zoom in(放大),
window.scrollXOffset
和window.scrollYOffset
应该会变化。但是browser本身对这个操作进行了内部处理,确保之前在可见视框顶部的元素仍然出现在顶部(尽管位置不一定是完美的),所以仍然会触发scroll
event,也就是说我们不需要考虑zoom可能产生的后果。
#backTop {
position: fixed;
bottom: 10px;
right: 10px;
}
let backTop = document.getElementById('backTop');
let ticking = false;
window.addEventListener('scroll', function(event) {
if (!ticking) {
window.requestAnimationFrame(function() {
if(window.pageYOffset > document.documentElement.clientHeight) backTop.hidden = false;
else backTop.hidden = true
ticking = false;
});
ticking = true;
}
})
对于第二题: 我没太懂补充的含义,我就按照自己理解来做了. 题意理解: body高度固定,div作为滚动容器, backTop 在div容器之外.
- 判断是否超过一页,使用的
element.scrollTop
和element.clientHeight
,仍然是高度不算scrollbar。 - 另外backTop直接就使用click来处理了,在移动端可以添加touch事件。
#backTop {
position: fixed;
bottom: 10px;
right: 10px;
}
#container {
position: relative;
margin: 20px;
width: 500px;
height: 500px;
border: 1px solid black;
overflow: scroll;
}
let backTop = document.getElementById('backTop');
let container = document.getElementById('container');
let ticking = false;
container.addEventListener('scroll', function(event) {
if (!ticking) {
window.requestAnimationFrame(function() {
if(container.scrollTop > container.clientHeight) backTop.hidden = false;
else backTop.hidden = true
ticking = false;
});
ticking = true;
}
})
backTop.addEventListener('click', function() {
container.scrollTop = 0;
})
html,body{
height:100%
}
<script>
const h = window.innerHeight // 一屏的高度
document.onscroll = () => {
fn()
}
var fn = dealwith( () => {
if (document.documentElement.scrollTop > h) {
document.querySelector('#backTop').removeAttribute('hidden')
} else {
document.querySelector('#backTop').setAttribute('hidden', '')
}
})
// 节流函数
function dealwith (fn) {
var timer
var lastTime = new Date()
return function () {
var now = new Date()
clearTimeout( timer )
if ( now - lastTime < 500 ) {
timer = setTimeout( () => {
fn()
lastTime = now
}, 300)
} else {
fn()
lastTime = now
}
}
}
</script>
第二题,有点难理解,而且不知道具体的需求 另外,请教大佬,这个要兼容移动端是啥意思啊?移动端浏览器跟pc不是只有屏幕的尺寸有问题嘛,内核啥的难道也有区别吗?
html,body{ height:100% }
<script> const h = window.innerHeight // 一屏的高度 document.onscroll = () => { fn() } var fn = dealwith( () => { if (document.documentElement.scrollTop > h) { document.querySelector('#backTop').removeAttribute('hidden') } else { document.querySelector('#backTop').setAttribute('hidden', '') } }) // 节流函数 function dealwith (fn) { var timer var lastTime = new Date() return function () { var now = new Date() clearTimeout( timer ) if ( now - lastTime < 500 ) { timer = setTimeout( () => { fn() lastTime = now }, 300) } else { fn() lastTime = now } } } </script>
第二题,有点难理解,而且不知道具体的需求 另外,请教大佬,这个要兼容移动端是啥意思啊?移动端浏览器跟pc不是只有屏幕的尺寸有问题嘛,内核啥的难道也有区别吗?
我记得 移动端对 scroll 事件有优化,当在滚动过程中,不会执行 scroll 事件绑定的函数;当滚动停止的时候才会执行。
html,body{ height:100% }
<script> const h = window.innerHeight // 一屏的高度 document.onscroll = () => { fn() } var fn = dealwith( () => { if (document.documentElement.scrollTop > h) { document.querySelector('#backTop').removeAttribute('hidden') } else { document.querySelector('#backTop').setAttribute('hidden', '') } }) // 节流函数 function dealwith (fn) { var timer var lastTime = new Date() return function () { var now = new Date() clearTimeout( timer ) if ( now - lastTime < 500 ) { timer = setTimeout( () => { fn() lastTime = now }, 300) } else { fn() lastTime = now } } } </script>
第二题,有点难理解,而且不知道具体的需求 另外,请教大佬,这个要兼容移动端是啥意思啊?移动端浏览器跟pc不是只有屏幕的尺寸有问题嘛,内核啥的难道也有区别吗?
我记得 移动端对 scroll 事件有优化,当在滚动过程中,不会执行 scroll 事件绑定的函数;当滚动停止的时候才会执行。
确切的说是 ios 的机型
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#container {
width: 100%;
height: 100%;
overflow: scroll;
}
<div id="container">
<a href="#" id="backTop" hidden>返回顶部!</a>
<div class="big"></div>
</div>
let backTop = document.getElementById('backTop')
let viewportHeight = document.documentElement.clientHeight || document.body.clientHeight
window.addEventListener('scroll', function(){
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
if(scrollTop >= viewportHeight){
if(backTop.hasAttribute('hidden')){
backTop.removeAttribute('hidden')
}
}else if(!backTop.hasAttribute('hidden')){
backTop.setAttribute('hidden', '')
}
}, false)
let container = document.getElementById('container')
container.addEventListener('scroll', function(){
let scrollTop = this.scrollTop
if(scrollTop >= viewportHeight){
if(backTop.hasAttribute('hidden')){
backTop.removeAttribute('hidden')
}
}else if(!backTop.hasAttribute('hidden')){
backTop.setAttribute('hidden', '')
}
}, false)
技术比较菜,不太懂 pc 和移动端关于兼容这块有什么具体的区别, 也没体会出上面使用节流函数的必要性是什么,我觉得没必要用防抖节流来控制啊
/*
* 调用:
* backTop({
* wrapEl: el, // 选传
* backTopEl: el
* })
**/
function backTop (obj) {
var wrapEl = obj.wrapEl || window, // 外层元素
backTopEl = obj.backTopEl, // 返回顶部
wH, // 外层元素高度
scrollTop, // 滚动高
timer1, timer2; // 定时器
(wrapEl===document.body || wrapEl===document.documentElement) && (wrapEl = window);
wrapEl===window && (wH=document.documentElement.clientHeight,1) || (wH=wrapEl.clientHeight);
if (backTopEl) {
wrapEl.onscroll = function () {
clearTimeout(timer2);
timer2 = setTimeout(function () { // 节流
scrollTop = wrapEl===window ? (document.documentElement.scrollTop || document.body.scrollTop) : (wrapEl.scrollTop);
(scrollTop-wH>0) ? backTopEl.removeAttribute("hidden") : backTopEl.setAttribute("hidden", "hidden");
}, 200);
}
window.onresize = function () {
clearTimeout(timer1);
timer1 = setTimeout(function () {
wrapEl===window && (wH=document.documentElement.clientHeight,1) || (wH=wrapEl.clientHeight);
}, 200);
}
}
}
let backTop = document.getElementById('backTop')
let scrollFunction = (box) => {
box.onscroll = () => {
let scrollT = box.scrollTop || document.documentElement.scrollTop || document.body.scrollTop
let clientH = box.clientHeight || document.documentElement.clientHeight
if (scrollT > clientH) {
backTop.style.display = 'block'
} else {
backTop.style.display = 'none'
}
}
}
scrollFunction(window) // 第一种
let divBox = document.getElementById('box')
scrollFunction(divBox) // 第二种
按照自己的理解初步实现了功能,细节方面还存在比较多的问题,请大家指正
第一题与第二题的函数
function scroll(node){
let parent = document.querySelector(node)
let h1,scrolltop ;
if(node!="body"){
h1 = parent.offsetHeight
parent.onscroll = function(){
scrolltop = parent.scrollTop
console.log(scrolltop)
checkHeight(h1,scrolltop)
}
}else{
h1 = document.documentElement.clientHeight
window.onscroll=function(){
scrolltop = document.documentElement.scrollTop||document.body.scrollTop;
console.log(scrolltop)
checkHeight(h1,scrolltop)
}
}
}
// 比较检查
function checkHeight(h1,h2){
let backtop = document.querySelector("#backTop")
if(h1>h2){
backtop.style.display="none"
}else{
backtop.style.display="block"
}
}
这里进行调用
//第一题
scroll("body")
//第二题是div盒子,盒子要写上overflow-y:scroll; 此盒字id为scrollBox
scroll("#scrollBox")
html
<body id="wrap" style="height: 1800px;">
<a href="#" id="backTop" style="position: fixed;top: 20px;right: 20px;" hidden>返回顶部!</a>
<a href="#" id="info" style="position: fixed;top:20px;left: 20px;"></a>
<div class="box" style="height: 200px;overflow: auto; width:200px;background-color: #ccc;">
<div class="con" style="height: 1800px">
</div>
</div>
</body>
js
function scroller(param) {
let {scrollDom, distance, backDom} = param
scrollDom = scrollDom || window
backDom = backDom || document.querySelector('#backTop')
let isDoc = scrollDom.nodeName === undefined || scrollDom.nodeName === 'HTML'
if (isDoc) {
scrollDom = window
}
scrollDom.addEventListener('scroll', () => {
let scrollTop
if (isDoc) {
scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
distance = distance || window.screen.height
} else {
scrollTop = scrollDom.scrollTop
distance = distance || scrollDom.clientHeight
}
document.querySelector('#info').innerHTML = scrollTop + ',' + distance
if (scrollTop > distance) {
backDom.removeAttribute('hidden')
} else {
backDom.setAttribute('hidden', 'hidden')
}
}, false)
}
调用JS
new scroller({
scrollDom: document.querySelector('.box'),
distance: 10,
backDom: document.querySelector('#backTop')
})
new scroller({scrollDom: document.querySelector('html')})
//new scroller({ distance: 100,})
第一题
整体思路:
- 需要用节流函数来控制scroll触发次数; 2. window.resize处理; 3. 滚动到顶部的按钮出现有一个阈值来控制
css
#scrollTop {
display: block;
visibility: hidden;
position: fixed;
bottom: 20px;
right: 0px;
}
#scrollTop.show {
visibility: visible
}
;(function () {
function fn() {
let $scrollTop = document.getElementById('scrollTop')
let top = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
top > 0 ? $scrollTop.classList.add('show') : $scrollTop.classList.remove('show')
}
// 窗口resize需要重新计算各高度
function resizeFn() {
// 实际内容高度
const totalHeight = document.documentElement.offsetHeight || document.body.offsetHeight
// 可视区域高度
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
let scrollFn = _.throttle(fn, 100);
// SCROLL_THRESHOLD是出现滚动到顶部的阈值
const SCROLL_THRESHOLD = 40;
if (totalHeight - windowHeight > SCROLL_THRESHOLD) {
window.removeEventListener('scroll', scrollFn);
window.addEventListener('scroll', scrollFn);
}
}
window.addEventListener('resize', _.throttle(resizeFn, 100));
resizeFn();
// 初始需要执行是因为部分浏览器刷新页面会定位到之前滚动的位置
fn()
})()
第二题
整体思路:
- 需要用节流函数来控制scroll触发次数; 2. 滚动到顶部的按钮出现有一个阈值来控制; 3. 回到顶部的样式要利用margin-top来实现类似fixed的效果
html
<div id="container">
<div id="body">
</div>
</div>
<a href="" id="scrollTop">回到顶部</a>
css
#scrollTop {
visibility: hidden;
position: absolute;
right: 10px;
margin-top: calc(20px - 200px); /*200px 是container的最大高度*/
}
#scrollTop.show {
visibility: visible
}
js
const $container = document.getElementById('container')
const $body = document.getElementById('body')
const $topBtn = document.getElementById('scrollTop')
const containerHeight = $container.clientHeight;
const bodyHeight = $body.offsetHeight;
// SCROLL_THRESHOLD是出现滚动到顶部的阈值
const SCROLL_THRESHOLD = 40;
if (bodyHeight - containerHeight > SCROLL_THRESHOLD) {
function scrollFn() {
$container.scrollTop > 0 ? $topBtn.classList.add('show') : $topBtn.classList.remove('show')
}
if (bodyHeight > containerHeight) {
$container.addEventListener('scroll', _.throttle(scrollFn, 100))
}
}
第 1 题和第 2 题,两大问题,一个对策。已封装成组件,只需为构造函数传入需要“返回顶部”功能的容器元素即可,默认为 body 元素。
在线演示
.back-top {
position: fixed;
margin-top: -15px;
margin-left: -15px;
transform: translate(-100%, -100%);
white-space: nowrap;
}
/**
* 返回顶部
* @descr: 给定一个容器,当滚动高度超出1倍的容器高度,显示返回顶部,否则隐藏
* @param {object HTMLElement} [elm] - HTML元素,可选,默认为 body 元素
*/
function ScrollBackTop(elm) {
var that = this;
var timer = null;
this.elm = elm || document;
this.elmBox = elm || document.documentElement;
this.render();
// 监听容器 scroll 事件
this.elm.addEventListener('scroll', function() {
clearTimeout(timer);
timer = setTimeout(function() {
that.scrolling();
}, 20);
}, false);
// 监听视窗尺寸改变
window.addEventListener('resize', function() {
clearTimeout(timer);
timer = setTimeout(function() {
that.scrolling();
}, 20);
}, false);
}
// 生成按钮
ScrollBackTop.prototype.render = function() {
var that = this;
var btn = document.createElement('a');
var text = document.createTextNode('返回顶部↑');
btn.className = 'back-top';
btn.href = '#';
btn.hidden = true;
// 监听点击
btn.addEventListener('click', function(event) {
if (that.elm === document) {
window.scrollTo(0, 0); // 兼容移动端
} else {
that.elmBox.scrollTop = 0;
}
event.preventDefault();
return false;
}, false);
btn.appendChild(text);
(this.elm === document ? document.body : this.elm).appendChild(btn);
this.btn = btn;
}
ScrollBackTop.prototype.scrolling = function() {
var elmBox = this.elmBox;
var scroll_y = this.elm === document ? window.pageYOffset : elmBox.scrollTop;
var offset_x = elmBox.offsetLeft;
var offset_y = elmBox.offsetTop;
var x = elmBox.clientWidth;
var y = elmBox.clientHeight;
if (scroll_y >= y) {
this.btn.hidden = false;
// 按钮的显示位置
this.btn.style.top = offset_y + y + 'px';
this.btn.style.left = offset_x + x + 'px';
} else {
this.btn.hidden = true;
}
}
new ScrollBackTop(); // 整个文档返回顶部的场景
new ScrollBackTop(document.getElementById('divBox')); // 某个 div 容器的场景
var viewHeight = -1; // 一屏的高度
var backTop = document.getElementById('backTop');
var lastModifiedBackTopHidden = true; // 上次缓存的结果,默认进入页面为隐藏状态
// 尺寸变化监听
function resizeObserver() {
var currentEl = this,
isDocumentNode = currentEl instanceof Document;
// 节流方法
window.requestAnimationFrame(function () {
viewHeight = isDocumentNode ? document.documentElement.clientHeight : Math.min(document.documentElement.clientHeight, currentEl.clientHeight)
});
}
// 通用监听
function observer() {
var currentEl = this,
isDocumentNode = currentEl instanceof Document;
// 偏移位置
var offset = isDocumentNode ? window.pageYOffset : currentEl.scrollTop
// 获取一屏的高度 只赋值一次
if (viewHeight < 0) {
resizeObserver.call(currentEl)
}
// 节流方法
window.requestAnimationFrame(function () {
var backTopHidden = offset > viewHeight;
if (lastModifiedBackTopHidden != backTopHidden) {
// 将上次比较的结果缓存,减少设置hidden的频次
lastModifiedBackTopHidden = backTopHidden;
backTop.hidden = !backTopHidden
}
});
}
// 第一题
document.addEventListener('scroll', observer)
// 第二题
document.getElementsByClassName('placeholder')[0].addEventListener('scroll', observer)
// resize 事件监听 当发生 resize 时,会导致一屏的高度变化
document.addEventListener('resize', resizeObserver);
// 初次加载页面时,需要判断滚动条是否在中间状态
document.addEventListener('DOMContentLoaded', observer);
代码片段:https://jsbin.com/katonod/2/edit?html,css,js,output
第一题
var topBtn = document.getElementById('bakcTop');
window.onscroll=function(){
var top = document.documentElement.scrollTop || document.body.scrollTop;
var height = document.documentElement.clientHeight;
if(top>=height){
topBtn.style.display="block"
}else{
topBtn.style.display="none"
}
}
第二题
var contanier = document.querySelectorAll(".contanier")[0];
var topBtn= document.getElementById("backTop")
contanier.onscroll=function(){
var height = contanier.clientHeight;
var top = contanier.scrollTop;
if(top>height){
topBtn.style.display="block"
}else{
topBtn.style.display="none"
}
}
- body
document.body.onscroll = function(){
var windowHeight = document.body.clientHeight;
var scrollTop = document.body.scrollTop;
document.getElementById("backTop").hidden = scrollTop > windowHeight?false:true;
};
- div
var contentBox = document.getElementById("content");
contentBox.onscroll = function(e){
var boxHeight = parseFloat(window.getComputedStyle(contentBox).height);
var scrollTop = e.target.scrollTop;
document.getElementById("backTop").hidden = scrollTop > boxHeight?false:true;
};
本地console的时候确实发现滚动事件触发频率很高,看到评论才发现还需要节流等等。。
基于CSS世界有6.4节 和 https://www.zhangxinxu.com/wordpress/2018/10/scroll-behavior-scrollintoview-平滑滚动
//随便创建个可以滚动的东西
const creatBody = (id, css) => {
const
div = document.createElement('div'),
ul = document.createElement('ul'),
uid = id || 'one',
cid = uid + '_fisrt'
for (let i = 0; i < 30; i++) {
let t = document.createElement('li')
if (i == 0) {
t.id = cid
}
t.innerHTML = `没什么意义的列表`
t.style.padding = '20px'
ul.appendChild(t)
}
const _css = css ? css : {
overflow: 'scroll',
height: '500px',
'scroll-behavior': "smooth",
}
ul.id = uid
addCSS({ position: 'relative' }, div)
addCSS(_css, ul)
div.appendChild(ul)
document.body.appendChild(div)
return ul
}
//创建回到顶部的按钮
const creatHandle = (el, html) => {
let handle = document.createElement('a')
handle.innerHTML = '回到顶部'
const css = {
position: html ? 'fixed' : 'absolute',
top: '70%',
right: '50px',
cursor: 'pointer',
border: '1px solid red',
display: 'none'
}
addCSS(css, handle)
//
if (typeof window.getComputedStyle(document.body).scrollBehavior == 'undefined') {
// 传统的JS平滑滚动处理代码...
console.log('Hellow Edge/IE')
console.log(el)
handle.onclick = backTop(el)
} else {
handle.href = '#' + (html ? el.id : el.childNodes[0].id)
}
if (html)
document.body.appendChild(handle)
else
el.appendChild(handle)
return handle
}
//js滚动动画,在edge试了下能用
const backTop = (el) => {
return e => {
let target
if (el.scrollTop) {
target = el
} else if (document.documentElement.scrollTop) {
target = document.documentElement
} else if (document.body.scrollTop) {
target = document.body
}
console.log("开始滚了")
cancelAnimationFrame(timer);
let timer = requestAnimationFrame(function fn() {
if (target.scrollTop > 0) {
target.scrollTop = target.scrollTop - 20;
timer = requestAnimationFrame(fn);
} else {
cancelAnimationFrame(timer);
}
});
}
}
//滚动事件
const showBackTop = (backTop) => {
return (el) => {
const
target = el.srcElement,//documentElement返回html根元素,再后面的是兼容edge
top = target.scrollTop || document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
cHeihgt = target.clientHeight || target.documentElement.clientHeight,
r = Math.floor(top / cHeihgt * 10)
// console.log(r)
// console.log('top:' + top)
if (r < 3 && backTop.style.display !== 'none') {
console.log('none')
addCSS({ display: 'none' }, backTop)
} else if (r >= 3 && backTop.style.display === 'none') {
console.log('该出现backTop了')
addCSS({ display: 'block' }, backTop)
}
}
}
//增加css
const addCSS = (obj, target) => {
//兼容下ie
if (!Object.entries) {
Object.entries = (obj) => {
let arr = [];
for (let key of Object.keys(obj)) {
arr.push([key, obj[key]]);
}
return arr;
}
}
for (let [key, values] of Object.entries(obj)) {
// console.log('key: ' + key + ' values: ' + values)
target.style[key] = values
}
}
addCSS({ 'scroll-behavior': "smooth" }, document.querySelector('html'))
const one = creatBody('one')
const two = creatBody(two, { padding: '50px' })
const h1 = creatHandle(one, false)
const h2 = creatHandle(two, true)
window.onscroll = showBackTop(h2)
one.onscroll = showBackTop(h1)
偷个懒写在一起啦
<style>
* {
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
height: 3000px;
}
#backTop{
position: fixed;
bottom: 10px;
right: 10px;
}
#bodyDiv {
height: 500px;
overflow: auto;
}
.content {
height: 3000px;
background-color: #ddd;
}
.content2 {
height: 1500px;
background-color: silver;
}
.body-border{
position: relative;
}
#backTop2{
position: absolute;
bottom: 10px;
right: 30px;
}
</style>
<body>
<div class="body-border">
<div id="bodyDiv">
<div class="content2">bodyDiv</div>
</div>
<a href="#" id="backTop2" onclick="backTop2()" hidden>返回顶部2</a>
</div>
<div class="content">body content</div>
<a href="#" id="backTop" onclick="backTop()" hidden>返回顶部</a>
</body>
var divBody = document.getElementById('bodyDiv')
var isHiddenBody = document.getElementById('backTop').getAttribute('hidden');
var isHiddenBody2 = document.getElementById('backTop2').getAttribute('hidden');
window.addEventListener('scroll', bodyScroll);
divBody.addEventListener('scroll', divScroll);
function bodyScroll (e) {
var windownHeight = window.screen.availHeight;
if (document.body.scrollHeight > windownHeight) {
var top = document.body.scrollTop || document.documentElement.scrollTop;
if (top >= windownHeight && isHiddenBody !== null) {
document.getElementById('backTop').removeAttribute('hidden');
isHiddenBody = null
} else if (top < windownHeight && isHiddenBody === null) {
document.getElementById('backTop').setAttribute('hidden', '');
isHiddenBody = ''
}
}
}
function divScroll (e) {
var divHeight = divBody.offsetHeight;
if (divBody.scrollHeight > divHeight) {
var top = divBody.scrollTop;
if (top >= divHeight && isHiddenBody2 !== null) {
document.getElementById('backTop2').removeAttribute('hidden');
isHiddenBody2 = null
} else if (top < divHeight && isHiddenBody2 === null) {
document.getElementById('backTop2').setAttribute('hidden', '');
isHiddenBody2 = ''
}
}
}
function backTop () {
document.getElementsByTagName('body')[0].scrollTop = 0;
}
function backTop2 () {
document.getElementById('bodyDiv').scrollTop = 0;
}
(function () {
function checkIsDom(obj) {
return obj && obj.nodeType && obj.nodeType === 1 && typeof obj.nodeName === 'string';
}
function ScrollTopCheck(container, target) {
if (!(this instanceof ScrollTopCheck)) {
return new ScrollTopCheck(container, target)
}
this.isDom = checkIsDom(container);
if (!(this.isDom || container === window) || !checkIsDom(target)) {
throw Error('container and target is required')
}
this.container = container;
this.target = target;
this.containerHeught = this.isDom
? parseInt(window.getComputedStyle(container).height)
: window.innerHeight;
this.init();
}
var fn = ScrollTopCheck.prototype;
fn.init = function () {
this.container.addEventListener('scroll', this.scroll.bind(this));
};
fn.scroll = function (e) {
var hasScrollHeight = this.isDom
? this.container.scrollTop
: this.container.scrollY;
if (hasScrollHeight > this.containerHeught) {
this.removeHidden()
} else {
this.addHidden();
}
};
fn.addHidden = function () {
this.target.setAttribute('hidden', 'true')
};
fn.removeHidden = function () {
this.target.removeAttribute('hidden')
};
window && (window.ScrollTopCheck = ScrollTopCheck)
})();
// 说明:使用方法,直接在window上调用ScrollTopCheck函数或者new ScrollTopCheck(),两个必穿参数,
// 第一个是容器,比如windo或者div,第二个为需要隐藏的标签,暂且都为必传。
// 暂为适配移动端。
// 写成构造函数的形式,感觉之后如果增加逻辑比较好扩展一些,条理也更加清楚,
// 放在一个自执行函数里,避免了过多的变量造成全局的污染
效果预览:https://output.jsbin.com/lezirisahu#
html有两个注意点:
第一页面要有 <!DOCTYPE html>
声明;
第二禁止页面缩放:<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
body,html {
scroll-behavior: smooth;
}
.btn {
position: fixed;
bottom: 0;
right: 0;
width: 100px;
height: 100px;
background: yellow;
border-radius: 50%;
line-height: 100px;
color: gray;
text-align: center;
text-decoration: none;
}
<body style="height:5000px;">
<a hidden id="backTop" href="#" class="btn">返回顶部</a>
<div id="box" style="background:pink;height:100px;overflow: scroll;">
我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div我这个div
</div>
function scroll(dom) {
var boxHeight = '',
scrollTop = '';
dom.onscroll = function() {
if (dom.nodeName == 'BODY') {
boxHeight = document.documentElement.clientHeight || dom.clientHeight;
scrollTop = document.documentElement.scrollTop || dom.scrollTop;
} else {
boxHeight = dom.clientHeight;
scrollTop = dom.scrollTop;
}
if (scrollTop > boxHeight) {
backTop.removeAttribute('hidden');
} else {
backTop.setAttribute('hidden', '');
}
};
}
var backTop = document.getElementById('backTop'),
boxContent = document.getElementById('box');
// body滚动调用
scroll(document.body);
// div滚动调用
scroll(boxContent);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
height: 2000px;
background-color: rgba(0,0,0,0.05);
}
#wrapper {
overflow: scroll;
background-color: rgba(0,0,0,0.05);
width: 500px;
height: 500px;
position: relative;
}
#content {
height: 1000px;
background-color: rgba(0,0,0,0.05);
}
#backTop1, #backTop2 {
position: sticky;
left: 0;
top: 200px;
}
</style>
</head>
<body>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Delectus officia earum expedita, iure qui aut, optio aspernatur, tenetur ad dolorum consequatur saepe excepturi itaque nesciunt molestias distinctio amet exercitationem hic.
<a href="#" id="backTop1" hidden>返回顶部↑</a>
<div id="wrapper" >
<div id="content"></div>
<a href="#" id="backTop2" hidden>返回顶部↑</a>
</div>
</body>
<script>
let backTop1 = document.getElementById('backTop1')
let backTop2 = document.getElementById('backTop2')
let wrapper = document.getElementById('wrapper')
let scorllTop = 0
let windowHeight = window.innerHeight || window.outerHeight || document.documentElement.clientHeight
let wrapperHeight = wrapper.innerHeight || wrapper.clientHeight
function throttle (func, time) {
let memo = null
return function (...args) {
if (!memo) {
memo = setTimeout(() => {
func(...args)
memo = null
}, time)
}
}
}
// 当容器为 window
document.addEventListener('scroll', throttle(() => {
scrollTop = document.documentElement.scrollTop
if(windowHeight < scrollTop) {
backTop1.removeAttribute('hidden')
} else {
backTop1.setAttribute('hidden', true)
}
}, 100))
// 当容器为某个元素
wrapper.addEventListener('scroll', throttle(() => {
scrollTop = wrapper.scrollTop
console.log(scorllTop)
if(wrapperHeight < scrollTop) {
backTop2.removeAttribute('hidden')
} else {
backTop2.setAttribute('hidden', true)
}
}, 100))
document.addEventListener('resize', () => {
windowHeight = window.innerHeight || window.outerHeight || document.documentElement.clientHeight
wrapperHeight = wrapper.innerHeight || wrapper.clientHeight
})
</script>
</html>