本文內容相當簡單,所以沒有發佈到博客園首頁,如果你不幸看到,那隻能是我這篇文章的榮幸,謝謝你的大駕光臨~
本文內容相當簡單,所以沒有發佈到博客園首頁,如果你不幸看到,那隻能是我這篇文章的榮幸,謝謝你的大駕光臨~(本博客返回頂部的功能就使用的是這個組件)
返回頂部組件是一種極其常見的網頁功能,需求簡單:頁面滾動一定距離後,顯示返回頂部的按鈕,點擊該按鈕可以將滾動條滾回至頁面開始的位置。實現思路也很容易,只要改變document.documentElement.scrollTop或document.body.scrollTop的值即可。本文拋棄所有加速減速的酷炫效果,回歸軟體的本質,提供一個最簡潔的實現,只追求實用性,不追求所謂的用戶體驗,效果如下(代碼下載):
由於思路跟代碼都很簡單,所以就直接貼出實現細節了:
var BackTop = function (domE,distance) { if (!domE) return; var _onscroll = window.onscroll, _onclick = domE.onclick; window.onscroll = throttle(function(){ typeof _onscroll === 'function' && _onscroll.apply(this, arguments); toggleDomE(); },100); domE.onclick = function(){ typeof _onclick === 'function' && _onclick.apply(this, arguments); document.documentElement.scrollTop = 0; document.body.scrollTop = 0; }; function toggleDomE(){ domE.style.display = (document.documentElement.scrollTop || document.body.scrollTop) > (distance || 500) ? 'block' : 'none'; } function throttle(func, wait) { var timer = null; return function () { var self = this, args = arguments; if (timer) clearTimeout(timer); timer = setTimeout(function () { return typeof func === 'function' && func.apply(self, args); }, wait); } } };
調用方式:
<script> new BackTop(document.getElementById('backTop')) </script>
之所以寫這篇博客,弄這麼個簡單的東西,有兩個方面的原因:
1)這段時間一直在手寫一些常見的簡單組件,這算是一個簡單中更簡單的一個,為了讓這系列的博客更加完整,所以把這個組件補充了進來;
2)我想表達自己在工作過程中的一個觀點:就是不要過渡用用戶體驗來裝飾你的軟體或者說產品,用戶體驗這個東西說白了就是兩個詞,一個是好印象,第二個就是好玩,但這並不是產品開發運營的最終目的,你把東西做的再漂亮,產品的核心價值和服務做的不夠的話,就算把返回頂部這種功能做成超級無敵的火箭也是徒勞無功的。做前端開發,得鍛煉點控制產品經理瞎提用戶體驗功能的度,以這個組件來說,我認為做加速或減速效果都是多餘的,既增加開發時間,又耽誤用戶使用的時間,拋棄自己心中那點對技術玩弄的固執,可以讓自己的工作做的更加完美。
最後,還是非常感謝你把它看完:)
補充於2016-03-16:
感謝普通男孩在評論中指出的問題:由於我提供的實現在註冊事件回調的時候用的onscroll和onclick的方式,雖然在組件內部會記錄這兩個事件之前可能註冊的回調,併在註冊組件的回調時,會先調用之前的回調,但是還存在另外一種情況:別人可能使用了這個組件,並且在這個組件之後也用onscroll或onclick給同一個對象註冊事件,如果沒把之前的回調調用一下,就會導致組件內部的回調被它後定義的回調覆蓋,導致組件失效。如果用addEventListener來註冊事件就不會有這個問題,因為它是可針對同一個事件註冊多個回調的,並且不存在覆蓋的問題,所以在此提供另外一個改良版的實現(對應代碼中的backTop2.js和index2.html):
var BackTop = function(domE, distance) { if (!domE) return; var AddListener = function(domE, type, fn) { if (typeof domE.addEventListener === 'function') { AddListener = function(domE, type, fn) { domE.addEventListener(type, fn, false); }; } else if (typeof el.attachEvent === 'function') { AddListener = function(domE, type, fn) { domE.attachEvent('on' + type, fn); }; } else { AddListener = function(domE, type, fn) { var old = el['on' + type]; el['on' + type] = function(){ typeof old === 'function' && old.apply(this, arguments); typeof fn === 'function' && fn.apply(this, arguments); }; }; } AddListener(domE, type, fn); } AddListener(window, 'scroll', throttle(function() { toggleDomE(); }, 100)); AddListener(domE, 'click', function() { document.documentElement.scrollTop = 0; document.body.scrollTop = 0; }) function toggleDomE() { domE.style.display = (document.documentElement.scrollTop || document.body.scrollTop) > (distance || 500) ? 'block' : 'none'; } function throttle(func, wait) { var timer = null; return function() { var self = this, args = arguments; if (timer) clearTimeout(timer); timer = setTimeout(function() { return typeof func === 'function' && func.apply(self, args); }, wait); } } };
註:以上代碼中的AddListener以及throttle的實現均可抽出來,作為單獨的工具函數使用,這樣組件會看起來更簡潔。這裡是為了組件的完整性,才把她們放在一塊。throttle是函數節流的簡單實現。在下一篇文章中有更詳細地關於該函數作用的介紹:利用getBoundingClientRect方法實現簡潔的sticky組件