Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 76 additions & 35 deletions jquery.scrollIntoView.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* The default browser behavior always places the element at the top or bottom of its container.
* This override is smart enough to not scroll if the element is already visible.
*
* Fix for if the immediate offset-parent is not scrollable
*
* Copyright 2011 Arwid Bancewicz
* Licensed under the MIT license
* http://www.opensource.org/licenses/mit-license.php
Expand All @@ -11,8 +13,46 @@
* @author Arwid Bancewicz http://arwid.ca
* @version 0.3
*/
(function($) {

(function($) {

function getViewportHeight(elem)
{
var pH = elem.clientHeight;
var wH = $(window).height();

if ((pH > wH && elem.tagName == "BODY") ||
(pH == 0 && elem.tagName == "BODY"))
return wH;
// case: if body's elements are all absolutely/fixed positioned, use window height
else
return pH;
}

function getScrollParent(elem)
{
//Find parent of interest, keep track of scroll offsets.
var scrollOffset = 0;
var pEl = elem;

// go up parents until we find one that scrolls

while (pEl) {
// Can we scroll this? (simpler check)
if (pEl.scrollHeight > getViewportHeight(pEl))
return pEl;

// try next parent
pEl = pEl.parentElement;
}

return undefined;
}

$.fn.scrollIntoView = function(duration, easing, complete) {
if (this.length == 0)
return;

// The arguments are optional.
// The first argment can be false for no animation or a duration.
// The first argment could also be a map of options.
Expand All @@ -29,39 +69,36 @@
opts.smooth = false;
}

// get enclosing offsets
var elY = Infinity, elH = 0;
if (this.size()==1)((elY=this.get(0).offsetTop)==null||(elH=elY+this.get(0).offsetHeight));
else this.each(function(i,el){(el.offsetTop<elY?elY=el.offsetTop:el.offsetTop+el.offsetHeight>elH?elH=el.offsetTop+el.offsetHeight:null)});
elH -= elY;

// start from the common ancester
// and find nearest scrollable parent.
var pEl = this.commonAncestor().get(0);

var wH = $(window).height();
pEl = getScrollParent(pEl);

// go up parents until we find one that scrolls
while (pEl) {
var pY = pEl.scrollTop, pH = pEl.clientHeight;
if (pH > wH) pH = wH;

// case: if body's elements are all absolutely/fixed positioned, use window height
if (pH == 0 && pEl.tagName == "BODY") pH = wH;

if (
// it wiggles?
(pEl.scrollTop != ((pEl.scrollTop += 1) == null || pEl.scrollTop) && (pEl.scrollTop -= 1) != null) ||
(pEl.scrollTop != ((pEl.scrollTop -= 1) == null || pEl.scrollTop) && (pEl.scrollTop += 1) != null)) {
if (elY <= pY) scrollTo(pEl, elY); // scroll up
else if ((elY + elH) > (pY + pH)) scrollTo(pEl, elY + elH - pH); // scroll down
else scrollTo(pEl, undefined) // no scroll
return;
}

// try next parent
pEl = pEl.parentNode;

// get enclosing offsets relative to scrollable parent.
var pY = pEl.getBoundingClientRect().top;
var pH = getViewportHeight(pEl);
var elTop = Infinity, elBot = -Infinity;
if (this.length==1)
{
var rect = this[0].getBoundingClientRect();
elTop = rect.top - pY;
elBot = rect.bottom - pY;
}

else this.each(function(i,el){
var rect = el.getBoundingClientRect();
elTop = Math.min(elTop, rect.top - pY);
elBot = Math.max(elBot, rect.bottom - pY);
});

//Scrolling
if (elTop < 0)
scrollTo(pEl, pEl.scrollTop + elTop); // scroll down
else if (elBot > pH)
scrollTo(pEl, pEl.scrollTop + (elBot - pH)); // scroll up
else
scrollTo(pEl, undefined) // no scroll

function scrollTo(el, scrollTo) {
if (scrollTo === undefined) {
if ($.isFunction(opts.complete)) opts.complete.call(el);
Expand All @@ -74,13 +111,13 @@
}
return this;
};

$.fn.scrollIntoView.defaults = {
smooth: true,
duration: null,
easing: $.easing && $.easing.easeOutExpo ? 'easeOutExpo': null,
// Note: easeOutExpo requires jquery.effects.core.js
// otherwise jQuery will default to use 'swing'
// otherwise jQuery will default to use 'swing'
complete: $.noop(),
step: null,
specialEasing: {} // cannot be null in jQuery 1.8.3
Expand All @@ -93,7 +130,12 @@
// completely? whether element is out of view completely
var outOfView = true;
this.each(function() {
var pEl = this.parentNode, pY = pEl.scrollTop, pH = pEl.clientHeight, elY = this.offsetTop, elH = this.offsetHeight;

var pEl = getScrollParent(this.parentNode),
pY = pEl.scrollTop,
pH = getViewportHeight(pEl),
elY = this.offsetTop,
elH = this.offsetHeight;
if (completely ? (elY) > (pY + pH) : (elY + elH) > (pY + pH)) {}
else if (completely ? (elY + elH) < pY: elY < pY) {}
else outOfView = false;
Expand Down Expand Up @@ -133,5 +175,4 @@
}
return $([]);
}

})(jQuery);
})(jQuery);