Абсолютно позиционированный div внутри прокрутки div прыгает

Я знаю, что подобные вопросы были, но пока я не нашел способ исправить свою проблему. Этот jsfiddle отражает мое требование.

Чтобы объяснить, внутри div есть несколько вертикально расположенных view-div (которые переполняются). У каждого view-div есть label-div, который должен отображаться в верхнем левом углу. Если view-divs прокручиваются горизонтально, метки должны оставаться в поле зрения. Когда представления прокручиваются по вертикали, представление, которое исчезает вверх, должно иметь метку, нажимаемую вниз, пока оно полностью не исчезнет.

Еще одно требование, которого нет в этом примере jsfiddle, заключается в том, что размеры view-div могут изменяться по вертикали (у меня есть готовый код, но я подумал, что он слишком велик для этого примера).

Теперь то, как я это реализовал, не работает. Этикетки двигаются недостаточно плавно. Я действительно хочу, чтобы они были приклеены к краю контейнера div. Кроме того, при быстрой прокрутке вверх ярлыки не попадают в верхний левый угол.

Некоторые другие вопросы/ответы SO предполагают, что я должен переключаться между фиксированным и абсолютным позиционированием в зависимости от направления прокрутки. Но я не думаю, что это сработает, потому что пользователь может перетаскивать прокручиваемый фон по горизонтали и вертикали одновременно.

Я надеялся, что подход $labels.css({"left" : scrLeft}) просто сработает, это казалось разумным. :-) Я попытался упростить мой пример, но и там движение слишком дерганое.

Любые идеи? Спасибо!!


person devboell    schedule 09.06.2013    source источник


Ответы (2)


Доказательство концепции решения

Это изящная небольшая проблема, которая действительно заставляет вас задуматься о различных перекрывающихся системах координат, которые управляют геометрией компоновки.

Вот jQuery:

//handle scrolling
var $labels = $(".label");

$("#container").on("scroll", function () {

    //horizontal
    var scrLeft = $(this).scrollLeft();
    $labels.css({
        "left": scrLeft
    });

    //vertical
    var scrollTop    = $("#container").scrollTop();
    var containerTop = $('#container').position().top;
    var innerTop = $('#inner_container').position().top;
    var views = $("#inner_container .view");
    var diff = 0;
    var heightAggr = 0;

    if (scrollTop == 0) {
        views.children(".label").css({
            "top": 0
        });
    }

    views.children(".label").removeClass('highlight');
    for (var i = 0; i < views.length; i += 1) {
        var view = $(views[i])
        var viewHeight = view.outerHeight();
        var viewTop = view.offset().top;

        /* This is the key parameter to test... 
           getting this relationship right took a lot of effort,
           everything else was relatively easy... */
        diff = scrollTop - viewTop + innerTop;

        if (diff>0) {
            view.children(".label").addClass('highlight');
            var labelOffset = scrollTop - heightAggr;
            var maxOffset = view.height() 
                            - view.children(".label").outerHeight();

            /* The following pins the label to the bottom of the
               view when view is about to scroll off the screen... */
            var labelPosition = Math.min(labelOffset,maxOffset); 

            view.children(".label").css({
            "top": labelPosition
            });
        } else {
            view.children(".label").css({ /* Clean up when         */
            "top": 0                      /* reverse scrolling ... */
            });
        }
        /* will allow .view with variable heights... */
        heightAggr += viewHeight; 
    }
});

Демонстрационная скрипта находится по адресу: http://jsfiddle.net/audetwebdesign/HRnCf/.

Отказ от ответственности в отношении браузера

Я тестировал это только в Firefox...

person Marc Audet    schedule 14.06.2013
comment
Кстати, вы можете проверить следующий заголовок stackoverflow.com/questions/17138438/ Еще один пример jQuery, над которым я работал вчера. Сначала выпейте кофе... - person Marc Audet; 17.06.2013

Вы должны использовать фиксированную позицию:

http://jsfiddle.net/mHWJH/1/

var $label1 = $("#label1"),
    offsetTop = $label1.offset().top,
    offsetLeft = $label1.offset().left,
    $label2 = $("#label2"),
    offsetTop2 = $label2.offset().top,
    offsetLeft2 = $label2.offset().left;

$("#container").on("scroll", function() {

    var scrLeft = $(this).scrollLeft();
    $label1.css({
        position:'fixed',
        top:offsetTop,
        left:offsetLeft
    });
     $label2.css({
        position:'fixed',
        top:offsetTop2,
        left:offsetLeft2
    });
});
person A. Wolff    schedule 09.06.2013
comment
да, я понимаю, что это сработает для простого примера. Реальный пример (см. первый абзац моего вопроса) немного сложнее. - person devboell; 09.06.2013