用原生 js && jquery 實現知乎收起答案功能

Build Statusnpm

  • Update 2016.12.7
  • 已封裝爲插件javascript

    • 原生 js 插件$ npm install foldcontent-zhihu@">=3.0.12" --save Usage
    • jquery 插件 $ npm install foldcontent-zhihu-jquery@">=1.0.1" --save Usage

  • Update 2016.11.23
  • 此前demo 定義兩個按鈕,一個固定定位,一個絕對定位,所以滾動至底部時會出現兩個按鈕同時出現的問題,用戶體驗不是很好,所以更改成只有一個按鈕,判斷滾動至底部時添加 classname 更改樣式。且精簡了代碼ヾ(≧∇≦*)ゝ
  • scroll 事件性能優化問題。css

    • 鼠標滾動時 scroll 事件觸發的間隔大約爲 10~20 ms,相對於其餘的鼠標、鍵盤事件,它被觸發的頻率很高,間隔很近。所以若是 scroll 事件涉及大量的位置計算、元素重繪等工做,且這些工做沒法在下個 scroll 事件觸發前完成,就會致使瀏覽器掉幀
    • 所以須要減小綁定給 scroll 中具體想要執行的業務邏輯的執行次數
    • 並將對象初始化、不變的高度值等緩存在 scroll 事件外部
    • 存在 bug : 當以很快的速度滾動時,有可能執行不到 scroll 綁定的事件(ó﹏ò),有沒得更好的優化方案?
  • 示例代碼及 github 均已更新 ٩(ˊᗜˋ*)و

  • Update 2016.11.22
  • 優化代碼結構,存在命名不規範、jquery 方法和原生 js 方法混用、代碼未封裝、設計冗餘等問題。。。review 代碼被批了。。。
    圖片描述

沒有需求的話……就本身提一個 ୧(๑•̀⌄•́๑)૭

  • 起初是要作一個公司內部的 mongoDB 日誌查詢網站,前端用 bootstrap,後端用 nodejs 作了一個簡單的頁面,不得不說頁面仍是很粗陋的,由於一條日誌的內容不少,若是直接顯示的話內容太過冗長,每每滾動幾回才能看完一條日誌,並且常常查詢的就是固定的幾個 key,直接展現不利於迅速debug。
  • 問題肯定了,要實現的就是顯示日誌時只顯示常常查詢的幾個鍵值對,點擊展開時顯示所有日誌,點擊收起時變回原狀。
  • 需求很簡單,並且和知乎的顯示所有&&收起功能很是類似,可是 Google 了一下沒有找到相似的 demo,所以決定本身實現一個!

Here we go

  • 看了知乎的網頁代碼。原理是點擊顯示所有時,若是這條答案超出瀏覽器視窗,則收起按鈕變成固定定位,js 計算出 right 值,bottom固定爲12px;當這條答案底部滾動至瀏覽器視窗內,收起按鈕變回絕對定位。ps 發現知乎是否是改版了,以前答案底部出如今瀏覽器視窗內後這個位置是有收起按鈕的?,按鈕從固定定位變爲絕對定位並更改樣式,就像旁邊的做者保留權利這樣的風格~(圖一直傳不上來。。暫時放棄了)
  • 如今的作法是直接隱藏掉固定定位的收起按鈕。

clipboard.png

  • 其實個人實現方法和知乎的不甚相同,由於他的 js 代碼我真心……沒看懂!誰能告訴我這種狀況應該怎麼調試(ノ°ο°)ノ

clipboard.png

  • 因此想了另外一種思路,在答案右下角定義一個按鈕 A,判斷答案頂部和底部的相對位置 x 和 y ,其中 y = x + 答案的高度(js 獲取)。當答案出如今瀏覽器視窗內,即 x = $(window).height() 時,給 A 添加固定定位,動態定義 right 值;當答案即將滾出瀏覽器視窗,即 y = $(window).height() 時,A 變爲相對定位,right 值始終爲 20px。當 A 的文本內容爲收起時,點擊 A 文本內容變爲展開,去掉固定定位。
  • 此處省去鏈接數據庫等無關工做,僅用兩段有趣的文字做爲 demo ~
  • 首先,文字內容分爲 all-content 和 part-content 兩部分,分別爲摺疊前和摺疊後要展現的內容,由於還未搞懂知乎摺疊答案後顯示哪一部份內容的算法,因此簡單粗暴地分了摺疊前和摺疊後的內容。。此處有一個 TODO ?
<ul class="wrap">
    <li>
        <div class="content all-content" style="display: none;">
            <h2>Sheldon 座位理論</h2>
            <p>In the winter that seat is close enough to the radiator to remain warm 
            and yet not so close as to cause perspiration. 
            In the summer it's directly in the path of a cross breeze 
            created by opening windows there, and there. 
            It faces the television at an angle that is neither direct, 
            thus discouraging conversation, 
            nor so far wide to create a parallax distortion. I could go on,
            but ... I think I've made my point.</p>
            <p>冬季的時候,這個地方離電暖器很近、最暖和,可是又不會近到讓你感受熱、流汗;
                夏天的時候,這裏又能夠吹由那兩扇窗戶吹進來的徐徐微風;
                坐這的角度可讓我同時看電視又同時和他人聊天而不受影響,
                剛好不會太遠也不會產生視覺上的錯覺。</p>
            <p>That is my spot. In an ever-changing world it is a simple point of consistency.
                If my life were expressed as a function in a four-dimensional Cartesian coordinate system,
                that spot, at the moment I first sat on it, would be 0000.</p>
            <p>那是個人專座。在這個不斷變化的世界裏,這是不變的一點。
                假設個人生命用一個創建在四維直角座標系裏的方程來表示的話,
                這個座位從我坐上那一刻開始就成爲了(0,0,0,0)。</p>
        </div>

        <div class="content part-content">
            In the winter that seat is close enough to the radiator to remain warm 
            and yet not so close as to cause perspiration. 
            In the summer
            <b> ...</b>
        </div>

        <div class="sign unfold">展開</div>
    </li>
</ul>
var doc = $(document);
var win = $(window);
// 屢次使用, 緩存起來
doc.on('click', '.unfold', function () {
    var unfold = $(this);
    if (unfold.text() !== '收起') {
        unfold.text('收起').siblings('.part-content').hide().siblings('.all-content').show();
        var panel = unfold.parent();
        var panelScroll = panel.offset().top + panel.height();
        var scrollHeight = doc.scrollTop() + win.height();
        var right = win.width() / 2 - 350 + 20 > 20 ? win.width() / 2 - 350 + 20 : 20;
        // 點擊展開按鈕時即判斷是否出現收起按鈕
        if (scrollHeight - panelScroll < 50) {
            unfold.addClass('fold-fix').css('right', right);
        }
        // 綁定在 scroll 事件上
        var cb = {
            onscroll: function() {
                var panelScroll = panel.offset().top + panel.height();
                var scrollHeight = doc.scrollTop() + win.height();
                var right = win.width() / 2 - 350 + 20 > 0 ? win.width() / 2 - 350 + 20 : 20;
                if (scrollHeight - panelScroll < 50 &&
                    panel.offset().top - scrollHeight < -90 && unfold.text() !== '展開') {
                    unfold.addClass('fold-fix').css('right', right);
                } else {
                    changeStyle(unfold);
                }
                win.off("scroll", cb.onscroll);
                setTimeout(function() {
                    win.on("scroll", cb.onscroll);
                }, 50);
            }
        };
        win.on("scroll", cb.onscroll);
    } else {
        var fold = $(this);
        changeStyle(fold);
        fold.text('展開').siblings('.part-content').show()
            .siblings('.all-content').hide();
    }
});

function changeStyle(i) {
    i.removeClass('fold-fix').css('right', '20px');
}
  • 此處涉及一個知識點:網頁元素的絕對位置 && 相對位置html

    • 網頁元素的絕對位置,指該元素的左上角相對於整張網頁左上角的座標。jquery 中 offset() 方法返回元素相對於文檔的偏移。該方法返回的對象包含兩個整型屬性:top 和 left。x.offset().top 即爲 x 元素的絕對高度;
    • 網頁元素的相對位置,指該元素左上角相對於瀏覽器窗口左上角的座標。絕對位置減去頁面的滾動條滾動的距離就是相對位置。x.offset().top - $(document).scrollTop() 即爲 x 元素的相對高度。
    • 本例須要瀏覽器視窗剛剛滾動至答案a的絕對定位按鈕出現的效果,所以此節點爲答案底部的相對高度減去瀏覽器視窗高度正好等於負的按鈕 A 的高度。即 a.offset().top + a.height() - $(document).scrollTop() - $(window).height() = - 按鈕高度
    • 阮一峯老師的用Javascript獲取頁面元素的位置這篇文章講解得十分清晰,若是要深刻了解這個知識點建議看一下這篇文章,說不定就有茅塞頓開的感受哦 ٩(ˊᗜˋ*)و
  • 如何動態設置固定定位的摺疊按鈕 的 right 值呢?前端

    • 答案的固定寬度是 700px,所以瀏覽器視窗寬度減去 700px 再除以 2 便始終是答案的 right 值,由於按鈕爲絕對定位時 right: 20px; 所以 right: $(window).width()/2 - 350 + 20 就保證了固定定位和絕對定位時按鈕都在一條垂直線上,過渡銜接很天然。
    • 當瀏覽器窗口不斷縮小時,上面計算出的固定定位時按鈕的 right 值可能爲負,這顯然不符合需求,所以要設置當計算出的 right 值爲負時設置 right 值爲 20px。

catch the code

  • 源代碼已上傳至 my github (ㆆᴗㆆ) ?
相關文章
相關標籤/搜索