滚动时冻结所选项目

Freeze selected item when scrolling

我有一个要求,当所选项目位于容器的顶部折叠中时,我需要将容器中项目列表中的所选项目冻结到顶部。当所选的项目位于容器的底部折叠中时,我需要将其粘在底部。

如果所选项目在可见折叠中,则不会发生任何事情。我的意思是所选项目应该与其他相邻项目正常流动。

我设法在某种程度上解决了这个问题。但是当我向上滚动时,当所选项目粘在容器上方时,所选项目隐藏了。即使当我向下滚动时,当所选项目粘在容器底部时,也会发生这种行为。

Here is the Fiddle

$('.item').click(function () {
    $('.item').removeClass('select').removeClass('pAbsolute');
    $(this).addClass('select');
});

$('.parent').scroll(function () {
    var $selected = $('.item.select');
    var cTop = $selected.offset().top;
    var cHeight = $selected.height();
    var pHeight = $(this).height();
    if (cTop < 0) {
        $selected.css({
            'top': $(this).scrollTop(),
                'bottom': ''
        }).addClass('pAbsolute');
    } else if (cTop > pHeight - cHeight) {
        $selected.css({
            'bottom': -$(this).scrollTop(),
                'top': ''
        }).addClass('pAbsolute');
    } else {
        $selected.css({
            'top': '',
                'bottom': ''
        }).removeClass('pAbsolute');
    }
});

当您 select 时,您必须使用一致的值来保持相对于容器的初始偏移量。

然后,计算偏移量和滚动值,

  1. 如果cTop < 0,表示它的顶部出箱,粘在顶部。

  2. 如果cTop + cHeight > pHeight,表示它的view超出bottom block,设置为bottom。

  3. 否则原地不动

编辑:

当select添加一个新的Item时,由于之前的item可能有.pAbsoluteattr,当前item的相对位置可能会发生变化,但是我们可以通过跟踪之前的偏移量来获取偏移量的变化在那些 class add/remove 操作之后。

然后我们可以通过手动更改容器的scrollTop来添加缺少的高度。

var offset;
$('.item').click(function () {
    // This is the offset in container before class change.
    offset = this.offsetTop;
    $('.item').removeClass('select').removeClass('pAbsolute');
    $(this).addClass('select');
    // Calculate the difference
    var distortion = offset - this.offsetTop;
    
    // Remove the distortion by manual scroll.
    var $parent = $(this).parent();
    $parent.scrollTop($parent.scrollTop() - distortion);
    
    offset = this.offsetTop;
});


$('.parent').scroll(function () {
    var $selected = $('.item.select');
    var cTop = offset - $(this).scrollTop();
    var cHeight = $selected.height();
    var pHeight = $(this).height();
    if (cTop < 0) {
        $selected.css({
            'top': $(this).scrollTop(),
                'bottom': ''
        }).addClass('pAbsolute');
    } else if (cTop + cHeight > pHeight) {
        $selected.css({
            'bottom': -$(this).scrollTop(),
                'top': ''
        }).addClass('pAbsolute');
    } else {
        $selected.css({
            'top': '',
                'bottom': ''
        }).removeClass('pAbsolute');
    }
});
body, html {
    padding: 0;
    margin: 0;
}
.parent {
    overflow: auto;
    height: 200px;
    position: relative;
}
.item {
    padding: 10px 15px;
    background-color: tomato;
    width: 100%;
}
.item.select {
    background-color: beige;
}
.pAbsolute {
    position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
    <div class="item select">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
    <div class="item">7</div>
    <div class="item">8</div>
    <div class="item">9</div>
    <div class="item">10</div>
    <div class="item">11</div>
    <div class="item">12</div>
    <div class="item">13</div>
    <div class="item">14</div>
    <div class="item">15</div>
    <div class="item">16</div>
    <div class="item">17</div>
    <div class="item">18</div>
    <div class="item">19</div>
    <div class="item">20</div>
    <div class="item">21</div>
    <div class="item">22</div>
    <div class="item">23</div>
    <div class="item">24</div>
    <div class="item">25</div>
    <div class="item">26</div>
    <div class="item">27</div>
    <div class="item">28</div>
    <div class="item">29</div>
    <div class="item">30</div>
    <div class="item">31</div>
    <div class="item">32</div>
    <div class="item">33</div>
    <div class="item">34</div>
    <div class="item">35</div>
    <div class="item">36</div>
    <div class="item">37</div>
    <div class="item">38</div>
    <div class="item">39</div>
    <div class="item">40</div>
    <div class="item">41</div>
</div>

此解决方案使用底部和顶部 header 填充所选值,必要时 showed/hidden:

Working Fiddle

Javascript:

function stickItems($parent, itemClass, selectClass) {
    // Attach dummy element items
    $parent.prepend('<div class="' + itemClass + ' sticky top"></div>');
    $parent.append('<div class="' + itemClass + ' sticky bottom"></div>');

    var $items = $('.' + itemClass),
        $stickyTop = $('.' + itemClass + '.sticky.top'),
        $stickyBottom = $('.' + itemClass + '.sticky.bottom');

    // Click event registering 
    $items.click(function (e) {
        if (!$(e.target).hasClass('sticky')) {
            $items.removeClass(selectClass);
            $stickyTop.css('display', 'none');
            $stickyBottom.css('display', 'none');
            $(this).addClass(selectClass);
        }
    });



    // Scroll event
    $parent.scroll(function () {
        var $self = $(this);
        var $selected = $('.' + itemClass + '.' + selectClass);
        var cTop = $selected.offset().top;
        var pTop = $self.offset().top;
        var cHeight = $selected.height();
        var pHeight = $self.height();
        if (cTop - pTop <= 0) {
            $stickyTop.html($selected.html()).css({
                'display': 'block',
                    'top': $(this).scrollTop()
            });
            $stickyBottom.css('display', 'none');
        } else if (cTop > pTop && cTop < pTop + pHeight) {
            $stickyTop.css('display', 'none');
            $stickyBottom.css('display', 'none');
        } else {
            $stickyTop.css('display', 'none');
            $stickyBottom.html($selected.html()).css({
                'display': 'block',
                    'bottom': -$(this).scrollTop()
            });
        }
    });
}

stickItems($('.parent'), 'item', 'select');

Css:

body, html {
    padding: 0;
    margin: 0;
}
body {
    padding-top: 200px;
}
.parent {
    overflow-x: hidden;
    overflow-y: auto;
    height: 200px;
    position: relative;
}
.item {
    padding: 10px 15px;
    background-color: tomato;
}
.item.select {
    background-color: beige;
}
.item.sticky {
    background-color: beige;
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    z-index: 1;
}

Html:

<div class="parent">
    <div class="item sticky top"></div>
    <div class="item select">1</div>
    <div class="item">2</div>
    <!-- ... -->
    <div class="item">39</div>
    <div class="item">40</div>
    <div class="item">41</div>
    <div class="item sticky bottom"></div>
</div>