DOM基礎小測27期答疑文字版-窗體滾動二三事
byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=8425
本文可全文轉載,個人網站無需授權,只要保留原作者、出處以及文中連結即可,任何網站均可摘要聚合,商用請聯絡授權。
一、題目與考察點
題目如下(紙質列印拍攝圖):
是相當簡單的一道題目,入門級別的,雖然挺簡單,但還有很多細節是很多人不知道的,因此還是很有價值的。
本題主要考察窗體滾動,窗體高度獲取,普通元素高度獲取這幾個知識點。
本次B站直播在上午月10:15分開始,持續約40分鐘,有錄播,可以直接下面瀏覽。
二、答疑內容
大家回答地址這裡: https://github.com/zhangxinxu/quiz/issues/4
1. 關於節流函式
有不少人答題時候使用了節流函式,如下:
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); } } }
程式碼高亮版本見下圖:
初衷是好的,滾動是一個高頻觸發的操作,通過節流函式可以降低計算方法執行的頻率。但是,實際上,我們開發的大多數頁面是如此的簡單,根本用不到需要節流函式,反而增加了程式碼的複雜度,對於本題,可以不需要。
2. 窗體滾動事件綁在哪裡?
瀏覽器窗體滾動事件綁在哪個物件上呢?是 window
物件,還是 document
物件,或者是 document.documentElement
, document.body
?
我們不妨測試下:
window.addEventListener('scroll', function () { console.log('window滾動觸發,window.pageYOffset是:' + this.pageYOffset); }); document.addEventListener('scroll', function () { console.log('document滾動觸發,document.scrollTop是:' + this.scrollTop); }); document.documentElement.addEventListener('scroll', function () { console.log('document.documentElement滾動觸發,document.documentElement.scrollTop是:' + this.scrollTop); }); document.body.addEventListener('scroll', function () { console.log('document.body滾動觸發,document.body.scrollTop是:' + this.scrollTop); });
您可以識別此二維碼:
或者直接點選這個頁面: 可以觸發scroll事情的滾動容器測試demo
結果無論是PC,還是移動端,測試結果如下:
也就是 window
物件和 document
物件繫結scroll事件可以觸發, document.documentElement
和 document.body
是不行的。
然後,直播的時候有人在群裡反饋,自己的手機 document
滾動無法觸發,如果這是真的,那安全起見,預設的瀏覽器窗體滾動事件還是繫結在 window
物件上。
3. 窗體的滾動高度獲取
如何獲取窗體的滾動高度呢?常見的有下面3種程式碼:
window.pageYOffset; document.documentElement.scrollTop; document.body.scrollTop;
都是有效的嗎?
我們不妨測試下,想辦法識別此二維碼:
或者直接訪問這個頁面: 3種窗體滾動高度獲取方法測試demo
結果在PC上是這樣:
而在手機上則是:
可以看到桌面瀏覽器和移動端瀏覽器對於滾動高度獲取是有差別的,桌面端瀏覽器不能使用 document.body.scrollTop
獲取瀏覽器窗體的滾動高度,而移動端不能使用 document.documentElement.scrollTop
獲取瀏覽器窗體的滾動高度。但是都支援 window.pageYOffset
。
所以,理論上講,瀏覽器窗體的滾動高度獲取使用 window.pageYOffset
即可,然而 window.pageYOffset
有一個缺點,就是IE9及其以上瀏覽器才支援,在PC端,很多專案是需要相容IE8瀏覽器的,因此,對於傳統PC網站,獲取瀏覽器窗體滾動高度比較好的表達方法是這樣:
var winScrollTop = window.pageYOffset || document.documentElement.scrollTop;
3. 瀏覽器窗體高度獲取
這個可以使用 window.innerHeight
獲取。然而, window.innerHeight
有相容性問題,IE8瀏覽器及其以下瀏覽器是不支援的,怎麼辦?可以藉助 document.documentElement.clientHeight
獲取。
於是:
pre>var winHeight = window.innerHeight || document.documentElement.clientHeight;
document.documentElement
是個很特殊的物件,他的很多行為表現跟普通元素是不一樣的。例如,普通div這類元素的 clientHeight
是不包括邊框大小的,但是, document.documentElement.clientHeight
直接無視這些,無論你 <html>
元素是否設定了 border
,都是頁面可視區域的高度。更神奇的是 document.documentElement.offsetHeight
居然是包含瀏覽器滾動高度的完整高度,等同於 document.documentElement.scrollHeight
,大家可以特殊記憶下。
4. 普通元素的滾動和高度獲取
普通元素的滾動直接新增scroll事件就好了,沒有任何相容性差異。
dom.addEventListener('scroll', function () { console.log('元素滾動觸發,滾動高度是:' + this.scrollTop); });
高度獲取則使用 clientHeight
,因為滾動的內容是不包括 border-box
的:
dom.addEventListener('scroll', function () { if (this.scrollTop > this.clientHeight) { console.log('滾動超過一屏了'); } });
對於普通元素的滾動高度獲取,還有很多其他的原生API,例如 offsetHeight
,包含 border
邊框大小; scrollHeight
包括滾動高度;getBoundingClientRect().height也是高度獲取,不會可以是小數,特殊場景挺有用的。
三、答題核心概要總結
- 窗體滾動使用window.addEventListener,document有人反饋不反應;
- 窗體滾動滾動高度獲取:window.pageYoffset(IE9+),
document.documentElement.scrollTop(PC),document.body.scrollTop(Mobile); - 普通元素直接scrollTop;
- 窗體高度獲取:window.innerHeight(IE9+),
備選方法為:document.documentElement.clientHeight; - 普通元素高度獲取:本題滾動事件中使用clientHeight(不含邊框,滾動是在border-box裡面的),
offsetHeight包含邊框,但是是整數;
getBoundingClientRect().height也包含邊框,可是是小數。(所有這幾個高度相關API都相容IE6+)
重要參考文件
本文提到的pageYoffset,innerHeight,clientHeight,offsetHeight,getBoundingClientRect等原生API都屬於CSSOM檢視模式(CSSOM View Module)相關內容,本文這裡只是一部分,更多內容可以參見本站經典文章:“ CSSOM檢視模式(CSSOM View Module)相關整理 ”。
下次直播預告
下週三群裡釋出CSS小測第2期正常,但是由於週六上班,有恰逢春節,因此直播答疑推遲到年後。想加入我的粉絲群的可以加我微信好友 zhangxinxu-job,我拉你們進去,備註“入群”,然後附上你們的姓名,方便我備註。
最後,祝廣大學友新年快樂,逢胸化吉。
本文為原創文章,會經常更新知識點以及修正一些錯誤,因此轉載請保留原出處,方便溯源,避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
本文地址: https://www.zhangxinxu.com/wordpress/?p=8425
(本篇完)