Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOM基础测试27 #4

Open
zhangxinxu opened this issue Jan 23, 2019 · 22 comments
Open

DOM基础测试27 #4

zhangxinxu opened this issue Jan 23, 2019 · 22 comments

Comments

@zhangxinxu
Copy link
Owner

zhangxinxu commented Jan 23, 2019

题目如下,入门级难度:

使用原生JS代码实现。

大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。

 ```js
// 你的JS代码写在这里
 ```

第二小题细节补充:

网页默认固定是HTML或者body,但有时候也可能是DIV,因为布局需要。和题1唯一不同就是容器变了。不要想太多。

@xiongchenf
Copy link

xiongchenf commented Jan 23, 2019

第一题:(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);
    }
  }
}

@WangNianyi2001
Copy link

WangNianyi2001 commented Jan 23, 2019

一个通吃两道小题的方案

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;
}

@liyongleihf2006
Copy link
Contributor

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);
    })
}

@XboxYan
Copy link

XboxYan commented Jan 23, 2019

借用一楼的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)

@BruceYuj
Copy link

BruceYuj commented Jan 23, 2019

对于第一题,分析提议需要注意以下几点:

  1. scroll event作为高触发事件,回调函数里面进行高损耗性能操作我们需要throttle,而throttle无外乎:requestAnimationFrame(),setTimeout() 或者CustomEvent
  2. 兼容PC端和移动端,使用window.pageYOffset而不是window.scrollY(处于兼容性考虑,其实目前应该所有主流浏览器都支持这两个属性对)。另外我们需要修改Mobile浏览器的默认layout viewport等于ideal viewport的大小(<meta name="viewport" content="width=device-width,initial-scale=1.0">
  3. 获取viewport高度时,我采用了document.documentElement.clientHeight,而没有采用window.innerHeight,是因为我不希望加上可能出现的x轴scrollbar的高度
  4. 是否考虑浏览器zoom in/out 操作,因为zoom操作很显然会改变浏览器的visual viewport dimension.理论上来讲,当用户scroll up之后再zoom in(放大), window.scrollXOffsetwindow.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容器之外.

  1. 判断是否超过一页,使用的element.scrollTopelement.clientHeight,仍然是高度不算scrollbar。
  2. 另外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;
})

@ghost
Copy link

ghost commented Jan 23, 2019

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 事件绑定的函数;当滚动停止的时候才会执行。

@ghost
Copy link

ghost commented Jan 23, 2019

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 的机型

@supperGod
Copy link

        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 和移动端关于兼容这块有什么具体的区别, 也没体会出上面使用节流函数的必要性是什么,我觉得没必要用防抖节流来控制啊

@LittleOtter
Copy link

/* 
* 调用:
*  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);
            }
        }
    }

@miaomiaolin
Copy link

miaomiaolin commented Jan 24, 2019

  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) // 第二种

按照自己的理解初步实现了功能,细节方面还存在比较多的问题,请大家指正

@sghweb
Copy link

sghweb commented Jan 24, 2019

第一题与第二题的函数

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")

@bb595700239
Copy link

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,})

@shirleyMHao
Copy link

shirleyMHao commented Jan 24, 2019

第一题

整体思路:

  1. 需要用节流函数来控制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()
})()
第二题

整体思路:

  1. 需要用节流函数来控制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))
  }
}

@wingmeng
Copy link

wingmeng commented Jan 24, 2019

第 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 容器的场景

@ghost
Copy link

ghost commented Jan 24, 2019

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

@liulyu
Copy link

liulyu commented Jan 24, 2019

第一题

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"
            }
        }

@boysama
Copy link

boysama commented Jan 24, 2019

  1. body
document.body.onscroll = function(){
    var windowHeight = document.body.clientHeight;
    var scrollTop = document.body.scrollTop;
    document.getElementById("backTop").hidden = scrollTop > windowHeight?false:true;
};
  1. 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的时候确实发现滚动事件触发频率很高,看到评论才发现还需要节流等等。。

@odex21
Copy link

odex21 commented Jan 24, 2019

基于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)

@momoxiaoqing
Copy link

偷个懒写在一起啦

<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;
    }

@wind1996
Copy link

wind1996 commented Jan 25, 2019

(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,第二个为需要隐藏的标签,暂且都为必传。
//  暂为适配移动端。
//  写成构造函数的形式,感觉之后如果增加逻辑比较好扩展一些,条理也更加清楚,
//  放在一个自执行函数里,避免了过多的变量造成全局的污染

@popeyesailorman
Copy link

popeyesailorman commented Jan 26, 2019

效果预览: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);

@zer0fire
Copy link

zer0fire commented Aug 12, 2020

<!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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests