Прокрутите страницу, чтобы элемент был виден

Я только что попробовал прототип scrollTo и, как говорится в документации, он

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

Мне нужна функция, которая

  • только прокручивает, если элемент не полностью виден в окне просмотра
  • прокручивается так, чтобы элемент находился в центре окна просмотра.

Кто-нибудь знает о такой функции в прототипе, сценарии или автономном?

3 ответа

Я думаю, вам нужно что-то вроде этого (demo):

window.height

function getWindowHeight() {
 var body = document.body;
 var docEl = document.documentElement;
 return window.innerHeight || 
 (docEl && docEl.clientHeight) ||
 (body && body.clientHeight) || 
 0;
}

Свиток

function scrollElemToCenter(id, duration) {
 var el = document.getElementById(id);
 var winHeight = getWindowHeight();
 var offsetTop = el.offsetTop;
 if (offsetTop > winHeight) { 
 var y = offsetTop - (winHeight-el.offsetHeight)/2;
 // wo animation: scrollTo(0, y);
 scrollToAnim(y, duration);
 }
}

Анимация (необязательно, вы можете использовать script.aculo.us и т.д.)

function interpolate(source,target,pos) { return (source+(target-source)*pos); }
function easing(pos) { return (-Math.cos(pos*Math.PI)/2) + 0.5; }
function scrollToAnim(targetTop, duration) {
 duration || (duration = 1000);
 var start = +new Date,
 finish = start + duration,
 startTop = getScrollRoot().scrollTop,
 interval = setInterval(function(){
 var now = +new Date, 
 pos = (now>finish) ? 1 : (now-start)/duration;
 var y = interpolate(startTop, targetTop, easing(pos)) >> 0;
 window.scrollTo(0, y);
 if(now > finish) { 
 clearInterval(interval);
 }
 }, 10);
}

получить корень прокрутки

var getScrollRoot = (function() {
 var SCROLL_ROOT;
 return function() {
 if (!SCROLL_ROOT) {
 var bodyScrollTop = document.body.scrollTop;
 var docElScrollTop = document.documentElement.scrollTop;
 window.scrollBy(0, 1);
 if (document.body.scrollTop != bodyScrollTop)
 (SCROLL_ROOT = document.body);
 else 
 (SCROLL_ROOT = document.documentElement);
 window.scrollBy(0, -1);
 }
 return SCROLL_ROOT;
 };
})();


Вот альтернативный подход, который использует некоторые из прототипов встроенных функций для работы с видовым окном и размерами прокрутки...

function scrollToCenterOfElement(id){
 // Cache element and property lookups...
 var element = $(id);
 var height = element.measure('height');
 var top = element.cumulativeOffset().top;
 var scroll = document.viewport.getScrollOffsets();
 var dimensions = document.viewport.getDimensions();
 // Checks to see if the top offset plus the height of the element is greater
 // than the sum of the viewport height and vertical scroll offset, which means
 // that the element has yet to be fully scrolled in to view, or if the 
 // top offset is smaller than the vertical scroll offset, which means the element
 // has already been (at least partly) scrolled out of view..
 if ((top + height > dimensions.height + scroll.top) || (top < dimensions.height + scroll.top)) {
 // Scroll window to sum of top offset plus half the height of the element
 // minus half of the viewport height, thus centering the element vertically.
 window.scrollTo(0, top + (height / 2) - (dimensions.height / 2));
 }
}
scrollToCenterOfElement('my-element');


Мое решение не покрывает 100% того, что запрашивается, но, возможно, кто-то считает его полезным.

/**
 * Scroll container so that given element becomes visible. Features:
 * <ol>
 * <li>If element is already visible, then no action is taken.
 * </li><li>If element is above view port, the viewport is scrolled upwards so that element becomes visible at the top.
 * </li><li>If element is below view port, the viewport is scrolled downwards so that element becomes visible at the bottom.
 * </li></ol>
 *
 * @param element
 * optional string (selector) or jQuery object that controls the scrolling of the element
 * @param options
 * optional extra settings
 * @param options.animationSpeed
 * if defined, then scrolling is animated; determines time in milliseconds after which the element should
 * be scrolled into viewport
 * @param options.heightScale
 * ****** number from 0 to 1; when scrolling the element from bottom sometimes it is desirable to scroll
 * element close to the top; e.g. to scroll it to the center specify 0.5; to scroll it to the top specify 0
 * @param options.complete
 * function to be called after animation is completed; if there is no animation, the function is called straight away
 */
$.fn.scrollTo = function(element, options) {
 options = options || {};
 var elementTop = element.offset().top;
 var containerTop = this.offset().top;
 var newScrollTop = null;
 if (elementTop < containerTop) {
 // Scroll to the top:
 newScrollTop = Math.round(this.scrollTop() + elementTop - containerTop);
 } else {
 // Scroll to the bottom:
 var elementBottom = elementTop + element.outerHeight(true);
 var containerHeight = this.height();
 if (elementBottom > containerTop + containerHeight) {
 if (options.heightScale != null) {
 if (options.heightScale === 0) {
 // This will effectively turn the formulae below into "elementTop - containerTop":
 containerHeight = element.outerHeight(true);
 } else {
 containerHeight *= options.heightScale;
 }
 }
 newScrollTop = Math.round(this.scrollTop() + elementBottom - containerTop - containerHeight);
 }
 }
 if (newScrollTop !== null) {
 if (options && options.animationSpeed) {
 this.animate({
 scrollTop : newScrollTop
 }, {
 "duration" : options.animationSpeed,
 "complete" : options.complete
 });
 } else {
 this.scrollTop(newScrollTop);
 if ($.isFunction(options.complete)) {
 options.complete();
 }
 }
 } else {
 if ($.isFunction(options.complete)) {
 options.complete();
 }
 }
 return this;
};

Демо

licensed under cc by-sa 3.0 with attribution.