Выделите элемент только в том случае, если он не отображается - jQuery

Я знаю, что вариация на этот вопрос была задана несколько раз; Я уже некоторое время просматриваю ТАК, но либо я делаю что-то не так, либо я не нашел то, что мне нужно.

У меня есть структура вложенных комментариев, почти такая же, как в плагине Facebook Comments, и всякий раз, когда нажимается reply, появляется маленькая форма с текстовой областью и кнопкой внизу комментариев.

Опять же, поведение аналогично плагину Facebook Comments, и я хочу добиться того же, когда дело доходит до прокрутки недавно добавленной текстовой области в поле зрения.

Я пробовал плагин scrollTo, и он работает плавно, но, даже если я прокручиваю вручную до самого низа страницы, анимация прокрутки всегда сбрасывает позицию прокрутки и начинается сверху.

Для записи, вот как я вызываю scrollTo:

$.scrollTo($('#addReply_1'), 800);

где addReply_1 - это div содержащий форму. Я пролистал прокрутку до самой формы и до textarea. Те же результаты.

Есть ли способ прокрутить элемент, только если он еще не виден?

Я перепробовал множество решений, предлагаемых в SO, например, прокрутку до элемента, использующего jQuery, но ни одно из них не работает так, как хотелось бы; даже прокрутка до определенного элемента с помощью jQuery или проверка, видим ли элемент после прокрутки, демонстрирует такое же "нервное" поведение.


ОБНОВЛЕНИЕ: онлайн демо, чтобы показать поведение

Я загрузил демонстрационную HTML-страницу, на которой показано поведение, на которое я жалуюсь: http://www.wouldbebetter.com/demo/comment-demo.htm

Просто прокрутите страницу вниз и нажмите любую из ссылок " Reply чтобы увидеть "нервную" прокрутку, на которую я ссылаюсь.

Обратите внимание, что это демо использует scrollintoview плагин из @Robert Koritnik ответа, но поведение такое же, если я использую, например, ScrollTo.

+34
источник поделиться
9 ответов

Да, есть плагин jQuery, который прокручивается к элементу только в том случае, если он не находится в пределах видимых границ прокручиваемого предка. Я написал, что делает именно то, что вам нужно. И вам, вероятно, будет проще использовать его по сравнению с scrollTo(), поскольку вам нужно предоставить только тот элемент, который вы хотели бы видеть.

Я мог бы скопировать код здесь, но поскольку я добавляю некоторые дополнения время от времени, лучше связать вас с блога, где вы 'найду все детали, связанные с прокруткой программ и последним кодом плагина. Программная прокрутка может отвлекать пользователей и весь пользовательский интерфейс, поэтому я предполагаю, что это будет интересное чтение.

Использование

Плагин очень прост в использовании:

$("#ElementToScrollIntoView").scrollintoview();

Плагин автоматически находит ближайшего прокручиваемого предка и прокручивает его соответствующим образом (если это вообще необходимо). Для этого плагина есть несколько дополнительных настроек, и они выглядят так:

scrollintoview: function (options) {
    /// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary>
    /// <param name="options" type="Object">Additional options that can configure scrolling:
    ///        duration (default: "fast") - jQuery animation speed (can be a duration string or number of milliseconds)
    ///        direction (default: "both") - select possible scrollings ("vertical" or "y", "horizontal" or "x", "both")
    ///        complete (default: none) - a function to call when scrolling completes (called in context of the DOM element being scrolled)
    /// </param>
    /// <return type="jQuery">Returns the same jQuery set that this function was run on.</return>

Я использую этот плагин на моем сайте Sharepoint 2010 на страницах, где представлены длинные табличные данные. Всякий раз, когда я добавляю новый элемент (строку) в эту таблицу, я дополнительно прокручиваю эту новую запись и выделяю ее, чтобы пользователи могли сразу увидеть новую запись.

Sharepoint была также причиной, по которой я решил не предоставлять элемент с возможностью прокрутки вручную, а искать его программно. Sharepoint использует admin costumizable master pages, что означает, что я не знаю, какой элемент будет прокручиваться во время выполнения. Но я знаю, какой элемент я хочу видеть. Отсюда этот плагин. Он также довольно упрощен по сравнению с плагином scrollTo(), который поддерживает различные сценарии. В большинстве случаев разработчики склонны использовать только одно (или очень ограниченное число из них).

Дополнительные наблюдения

Предотвращение обработки ссылок по умолчанию

Использование моих плагинов все еще делает его довольно проблематичным, так как некоторые мерцания при добавлении этих ответных полей. Проблема в том, что на самом деле выполняется ваша ссылка. Вы должны предотвратить это, чтобы ваша страница работала гладко:

  • либо установите события click на ваших ссылках одним из двух способов:

    <a href="javascript:void AddReplyForm(44); return false;">Reply</a>
    

    или

    <a href="#" onclick="void AddReplyForm(44); return false;">Reply</a>
    
  • лучшим способом было бы запустить это на готовом документе:

    $(function() {
        $("a").click(function(evt) {
            evt.preventDefault();
        });
    });
    

Основная идея - запретить браузеру обрабатывать ссылки на клики. Поскольку это заставляет браузер искать привязку на странице, и поскольку он не может найти его, он автоматически прокручивается вверх. Затем вы просите его прокрутить до элемента.

Дублирующие идентификаторы

При создании формы ответа вы добавляете новые, новые и новые элементы, но все они имеют одинаковый идентификатор. Вы должны либо не делать этого, либо использовать другие средства. Вы можете полностью удалить идентификаторы, предоставив элемент вашей функции BindClick(). Основная функция генерации ответа также могла бы выглядеть так (эта функция написана таким образом, чтобы полностью исключить необходимость в идентификаторах элементов):

function AddReplyForm(topCommentID)
{
    var el = $(addReplyForm).appendTo('#comment_' + topCommentID + ' .right');
    BindClick(el); // mind this !! you provide the element to remove
    el.scrollintoview();
}
+31
источник

Имела ту же проблему... после рассмотрения нескольких ответов, вот что я придумал для этого... не отрывая еще один плагин.

function scrollIntoViewIfNeeded($target) {
    if ($target.position()) {
        if ($target.position().top < jQuery(window).scrollTop()){
            //scroll up
            $('html,body').animate({scrollTop: $target.position().top});
        }
        else if ($target.position().top + $target.height() >
            $(window).scrollTop() + (
                window.innerHeight || document.documentElement.clientHeight
            )) {
            //scroll down
            $('html,body').animate({scrollTop: $target.position().top -
                (window.innerHeight || document.documentElement.clientHeight)
                    + $target.height() + 15}
            );
        }
    }
}

"15" на последней строке - это просто дополнительное дополнение - вам может потребоваться настроить его или добавить в строку прокрутки вверх.

EDIT: изменено window.innerHeight на (window.innerHeight || document.documentElement.clientHeight) для IE < 8 поддержка

+22
источник
другие ответы

Связанные вопросы


Похожие вопросы

Все современные браузеры поддерживают это. Посещение: http://caniuse.com/#search=scrollIntoView

function scrollIntoViewIfNeeded(target) {
    var rect = target.getBoundingClientRect();
    if (rect.bottom > window.innerHeight) {
        target.scrollIntoView(false);
    }
    if (rect.top < 0) {
        target.scrollIntoView();
    } 
}

Обновление

Цель должна быть элементом. Если вы используете jQuery, вызовите функцию следующим образом:

scrollIntoViewIfNeeded($(".target")[0]);
+21
источник

чтобы убедиться, что элемент находится внутри контейнера:

let rectElem = elem.getBoundingClientRect(), rectContainer=container.getBoundingClientRect();
if (rectElem.bottom > rectContainer.bottom) elem.scrollIntoView(false);
if (rectElem.top < rectContainer.top) elem.scrollIntoView();
+4
источник

Я немного изменил ответ от @iandisme и обернул его как крошечный плагин jquery:

(function ($) {
'use strict';

$.fn.scrollToSimple = function ($target) {
    var $container = this.first();      // Only scrolls the first matched container

    var pos = $target.position(), height = $target.outerHeight();
    var containerScrollTop = $container.scrollTop(), containerHeight = $container.height();
    var top = pos.top + containerScrollTop;     // position.top is relative to the scrollTop of the containing element

    var paddingPx = containerHeight * 0.15;      // padding keeps the target from being butted up against the top / bottom of the container after scroll

    if (top < containerScrollTop) {     // scroll up                
        $container.scrollTop(top - paddingPx);
    }
    else if (top + height > containerScrollTop + containerHeight) {     // scroll down
        $container.scrollTop(top + height - containerHeight + paddingPx);
    }
};
})(jQuery);

Я удалил вызовы .animate, так как искал мгновенную прокрутку. Я также добавил возможность прокрутки любого (прокручиваемого) контейнера, а не только окна. Пример использования:

// scroll the window so #target  is visible
$(window).scrollToSimple( $("#target") );

// scroll the scrollable element #container so that #target is visible
$("#container").scrollToSimple( $("#target") );
+2
источник

Я не знаю, понял ли я то, что вы хотите, но посмотрите, все ли это, близко или полностью потеряно:

<!DOCTYPE html>
<html>
    <head>
        <title>Scroll To Reply</title>
        <meta charset="utf-8" />
     <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
        <script type="text/javascript">
            $(document).ready(function(){
                var $contextarea = $('#contextform textarea');
                $('a.reply').live('click',function(){
                    $(this).closest('p').css({'margin-top':'300px;'});
                    $('#contextform').appendTo($(this).closest('p'));
                    $('#contextform').slideDown(1000);//.css({'display':'block'});
                    $(this).closest('p').attr('id','scrolltome');
                    $('html,body').animate({slideDown: $('#scrolltome').offset().top}, 2000);
                });
                $('.sendreply').live('click',function(){
                    if($contextarea.val() == null || $contextarea.val() == '') {
                        alert('textarea is empty!');
                    } else {
                        $('#contextform').slideUp(800);
                    }
                });

                //                $('button').click(function() {
                //                    $('html,body').animate({scrollTop: $('#body').offset().top}, 2000);//scrolls to the top
                //                });

            });
        </script>
        <style type="text/css">
            body{
                font-size:25px;
                font-family: 'Arimo', Arial, sans-serif;
            }
            #contextform {
                display:none;
                width:500px;
                height: 150px;
                background: #0489B7;
                padding: 5px
            }
            #contextform textarea {
                width: 490px;
                height: 90px;
            }
        </style>
    </head>
    <body id="body">
        <h1>Scroll to reply</h1>
        <div id="contextform">
            <form method="post" action="">
                <textarea id="textarea"></textarea>
                <button type="button" class="sendreply">Send Reply</button>
            </form>
            <a name="myAnchor" id="myAnchor">anchor</a>
        </div>
        <ol>
            <?php for ($i = 0; $i < 20; $i++) { ?>
                <li><p>The quick brown fox jumps over the lazy dog
                        <a href="#scrolltome" class="reply">Reply</a>
                    </p></li>
            <?php } ?>
        </ol>
    </body>
</html>
0
источник

Каждый ответ здесь либо казался устаревшим и больше не работал с современными версиями jQuery (возможно, из-за изменений в функциях position() и offset(), например), либо слишком ограниченным, чтобы работать в обстоятельствах, для которых они мне были нужны. Например, ни один из приведенных выше ответов не работает, если ваш код был в iframe.

Одна из самых больших вещей, которые я заметил, это то, что все они просто использовали обычную высоту объекта-контейнера, которая работала нормально, пока весь объект-контейнер был виден в окне, но когда вашим объектом-контейнером был сам объект html и высота развернутый намного ниже того, что было показано, прокручиваемая часть кода больше не работала. Вместо этого алгоритм должен использовать видимую высоту объекта на экране, чтобы он работал правильно (см. Получение видимой высоты элемента div с помощью jQuery).

Я закончил тем, что написал свое собственное решение, которое кажется намного более надежным и работает в большем количестве обстоятельств:

function scrollIntoViewIfNeeded($target, options) {

    var options = options ? options : {},
    $win = $($target[0].ownerDocument.defaultView), //get the window object of the $target, don't use "window" because the element could possibly be in a different iframe than the one calling the function
    $container = options.$container ? options.$container : $win,        
    padding = options.padding ? options.padding : 20,
    elemTop = $target.offset().top,
    elemHeight = $target.outerHeight(),
    containerTop = $container.scrollTop(),
    //Everything past this point is used only to get the container visible height, which is needed to do this accurately
    containerHeight = $container.outerHeight(),
    winTop = $win.scrollTop(),
    winBot = winTop + $win.height(),
    containerVisibleTop = containerTop < winTop ? winTop : containerTop,
    containerVisibleBottom = containerTop + containerHeight > winBot ? winBot : containerTop + containerHeight,
    containerVisibleHeight = containerVisibleBottom - containerVisibleTop;

    if (elemTop < containerTop) {
        //scroll up
        if (options.instant) {
            $container.scrollTop(elemTop - padding);
        } else {
            $container.animate({scrollTop: elemTop - padding}, options.animationOptions);
        }
    } else if (elemTop + elemHeight > containerTop + containerVisibleHeight) {
        //scroll down
        if (options.instant) {
            $container.scrollTop(elemTop + elemHeight - containerVisibleHeight + padding);
        } else {
            $container.animate({scrollTop: elemTop + elemHeight - containerVisibleHeight + padding}, options.animationOptions);
        }
    }
}

$target - это объект jQuery, содержащий объект, который вы хотите прокрутить, если необходимо.

options (необязательно) могут содержать следующие параметры, передаваемые в объекте:

options.$container - объект jQuery, указывающий на содержащий элемент $ target (другими словами, на элемент в dom с полосами прокрутки). По умолчанию используется окно, которое содержит элемент $ target и достаточно умное, чтобы выбрать окно iframe. Не забудьте указать $ в имени свойства.

options.padding - отступ в пикселях для добавления над или под объектом, когда он прокручивается в поле зрения. Таким образом, это не прямо на краю окна. По умолчанию 20.

options.instant - если задано значение true, jQuery animate не будет использоваться, и прокрутка мгновенно откроется в нужном месте. По умолчанию false.

options.animationOptions - любые параметры jQuery, которые вы хотите передать в функцию анимирования jQuery (см. http://api.jquery.com/animate/). При этом вы можете изменить продолжительность анимации или запустить функцию обратного вызова после завершения прокрутки. Это работает, только если для options.instant установлено значение false. Если вам нужна мгновенная анимация, но с обратным вызовом, установите options.animationOptions.duration = 0 вместо использования options.instant = true.

0
источник

Element.scrollIntoViewIfNeeded()

Согласно документам MDN Web:

Метод Element.scrollIntoViewIfNeeded() прокручивает текущий элемент в видимой области окна браузера, если он еще не находится в видимой области окна браузера. Если элемент уже находится в видимой области окна браузера, прокрутка не происходит. Этот метод является запатентованным вариантом стандартного метода Element.scrollIntoView().

Пример (с jQuery):

$('#example')[0].scrollIntoViewIfNeeded();

Примечание. Эта функция нестандартна и не соответствует стандартам, поэтому будьте осторожны, прежде чем использовать ее в производстве. смотрите Могу ли я использовать... для поддержки браузера.

0
источник

JQuery не требуется для этого.

Эта функция просто показывает указанный элемент:

function scrollIntoView(elm) {
    if(elm) {
        let bnd=elm.getBoundingClientRect();
        if     (bnd.top<0                    ) { elm.scrollIntoView(true );      }
        else if(bnd.bottom>window.innerHeight) { elm.scrollIntoView(bnd.top<=0); }
        }
    return elm;
    }

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

/**
  * Scroll the specified element into view, optionally first searching for a specific container and
  * first making that visible. This function does it best to scroll the entire container into view
  * but ultimately ensures that as much of the element as fits in the viewport will be visible.
  *
  * #### Arguments:
  *
  *     elm (DOMElement)    The element to make visible.
  *     contag (string)     Optional name of a container tag. Ignored if blank/null/omitted.
  *     conprp (string)     Optional name of a container property to also match. Ignored if blank/null/omitted.
  *     conval (string)     Optional value of the container property. Ignored if 'conprp' is not supplied; defaults to "" if omitted.
  */
function scrollIntoView(elm,contag,conprp,conval) {
    if(elm) {
        if(contag || conprp) {
            let con;
            if(conval==null) { conval=""; }
            for(con=elm; con!=null && con.tagName!="BODY"; con=con.parentNode) {
                if((!contag || contag==con.tagName) && (!conprp || con[conprp]==conval)) {
                    break;                                                                          // matched container tag and property
                    }
                }
            if(con) { scrollIntoView(con); }
            }

        let bnd=elm.getBoundingClientRect();

        if     (bnd.top<0                    ) { elm.scrollIntoView(true );      }
        else if(bnd.bottom>window.innerHeight) { elm.scrollIntoView(bnd.top<=0); }
        }
    return elm;
    }

Это позволяет, например, показать это при прокрутке вверх:

enter image description here

вместо этого:

enter image description here

-1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос