用原生 js && jquery 实现知乎收起答案功能

《用原生 js && jquery 实现知乎收起答案功能》《用原生 js && jquery 实现知乎收起答案功能》

  • Update 2016.12.7
  • 已封装为插件

    • 原生 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 事件性能优化问题。

    • 鼠标滚动时 scroll 事件触发的间隔大约为 10~20 ms,相对于其他的鼠标、键盘事件,它被触发的频率很高,间隔很近。因此如果 scroll 事件涉及大量的位置计算、元素重绘等工作,且这些工作无法在下个 scroll 事件触发前完成,就会导致浏览器掉帧
    • 因此需要减少绑定给 scroll 中具体想要执行的业务逻辑的执行次数
    • 并将对象初始化、不变的高度值等缓存在 scroll 事件外部
    • 存在 bug : 当以很快的速度滚动时,有可能执行不到 scroll 绑定的事件(ó﹏ò),有没得更好的优化方案?
  • 示例代码及 github 均已更新 ٩(ˊᗜˋ*)و
  • Update 2016.11.22
  • 优化代码结构,存在命名不规范、jquery 方法和原生 js 方法混用、代码未封装、设计冗余等问题。。。review 代码被批了。。。
    《用原生 js && jquery 实现知乎收起答案功能》

没有需求的话……就自己提一个 ୧(๑•̀⌄•́๑)૭

  • 起初是要做一个公司内部的 mongoDB 日志查询网站,前端用 bootstrap,后端用 nodejs 做了一个简单的页面,不得不说页面还是很粗陋的,因为一条日志的内容很多,如果直接显示的话内容太过冗长,往往滚动几次才能看完一条日志,而且经常查询的就是固定的几个 key,直接展示不利于迅速debug。
  • 问题确定了,要实现的就是显示日志时只显示经常查询的几个键值对,点击展开时显示全部日志,点击收起时变回原状。
  • 需求很简单,而且和知乎的显示全部&&收起功能非常相似,但是 Google 了一下没有找到类似的 demo,因此决定自己实现一个!

Here we go

  • 看了知乎的网页代码。原理是点击显示全部时,如果这条答案超出浏览器视窗,则收起按钮变成固定定位,js 计算出 right 值,bottom固定为12px;当这条答案底部滚动至浏览器视窗内,收起按钮变回绝对定位。ps 发现知乎是不是改版了,之前答案底部出现在浏览器视窗内后这个位置是有收起按钮的?,按钮从固定定位变为绝对定位并更改样式,就像旁边的作者保留权利这样的风格~(图一直传不上来。。暂时放弃了)
  • 现在的做法是直接隐藏掉固定定位的收起按钮。

《用原生 js && jquery 实现知乎收起答案功能》

  • 其实我的实现方法和知乎的不甚相同,因为他的 js 代码我真心……没看懂!谁能告诉我这种情况应该怎么调试(ノ°ο°)ノ

《用原生 js && jquery 实现知乎收起答案功能》

  • 所以想了另一种思路,在答案右下角定义一个按钮 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');
}
  • 此处涉及一个知识点:网页元素的绝对位置 && 相对位置

    • 网页元素的绝对位置,指该元素的左上角相对于整张网页左上角的坐标。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 (ㆆᴗㆆ) ?
    原文作者:一颗板栗
    原文地址: https://segmentfault.com/a/1190000007503399
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞