Last active
June 17, 2019 10:03
-
-
Save jcchikikomori/1cc91a4edd58b0991f67fd25cf9340e5 to your computer and use it in GitHub Desktop.
jQuery scrollintoview() plugin MIT Licensed - http://erraticdev.blogspot.com/2011/02/jquery-scroll-into-view-plugin-with.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* jQuery scrollintoview() plugin and :scrollable selector filter | |
* | |
* Version 1.9 (17 Jun 2019) | |
* Requires jQuery 1.4 or newer | |
* | |
* Forked version (1.8) by John Cyrill Corsanes | |
* https://gist.github.com/jcchikikomori/1cc91a4edd58b0991f67fd25cf9340e5 | |
* | |
* Copyright (c) 2011 Robert Koritnik | |
* Licensed under the terms of the MIT license | |
* http://www.opensource.org/licenses/mit-license.php | |
*/ | |
(function ($) { | |
var converter = { | |
vertical: { x: false, y: true }, | |
horizontal: { x: true, y: false }, | |
both: { x: true, y: true }, | |
x: { x: true, y: false }, | |
y: { x: false, y: true } | |
}; | |
var settings = { | |
duration: "fast", | |
direction: "both", | |
margin: 0 | |
}; | |
var rootrx = /^(?:html)$/i; | |
// gets border dimensions | |
var borders = function (domElement, styles) { | |
styles = styles || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(domElement, null) : domElement.currentStyle); | |
var px = document.defaultView && document.defaultView.getComputedStyle ? true : false; | |
var b = { | |
top: (parseFloat(px ? styles.borderTopWidth : $.css(domElement, "borderTopWidth")) || 0), | |
left: (parseFloat(px ? styles.borderLeftWidth : $.css(domElement, "borderLeftWidth")) || 0), | |
bottom: (parseFloat(px ? styles.borderBottomWidth : $.css(domElement, "borderBottomWidth")) || 0), | |
right: (parseFloat(px ? styles.borderRightWidth : $.css(domElement, "borderRightWidth")) || 0) | |
}; | |
return { | |
top: b.top, | |
left: b.left, | |
bottom: b.bottom, | |
right: b.right, | |
vertical: b.top + b.bottom, | |
horizontal: b.left + b.right | |
}; | |
}; | |
var dimensions = function ($element) { | |
var win = $(window); | |
var isRoot = rootrx.test($element[0].nodeName); | |
return { | |
border: isRoot ? { top: 0, left: 0, bottom: 0, right: 0} : borders($element[0]), | |
scroll: { | |
top: (isRoot ? win : $element).scrollTop(), | |
left: (isRoot ? win : $element).scrollLeft() | |
}, | |
scrollbar: { | |
right: isRoot ? 0 : $element.innerWidth() - $element[0].clientWidth, | |
bottom: isRoot ? 0 : $element.innerHeight() - $element[0].clientHeight | |
}, | |
rect: (function () { | |
var r = $element[0].getBoundingClientRect(); | |
return { | |
top: isRoot ? 0 : r.top, | |
left: isRoot ? 0 : r.left, | |
bottom: isRoot ? $element[0].clientHeight : r.bottom, | |
right: isRoot ? $element[0].clientWidth : r.right | |
}; | |
})() | |
}; | |
}; | |
$.fn.extend({ | |
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> | |
options = $.extend({}, settings, options); | |
options.direction = converter[typeof (options.direction) === "string" && options.direction.toLowerCase()] || converter.both; | |
var dirStr = ""; | |
if (options.direction.x === true) dirStr = "horizontal"; | |
if (options.direction.y === true) dirStr = dirStr ? "both" : "vertical"; | |
var el = this.eq(0); | |
var scroller = el.closest(":scrollable(" + dirStr + ")"); | |
// check if there's anything to scroll in the first place | |
if (scroller.length > 0) | |
{ | |
scroller = scroller.eq(0); | |
var dim = { | |
e: dimensions(el), | |
s: dimensions(scroller) | |
}; | |
var rel = { | |
top: dim.e.rect.top - (dim.s.rect.top + dim.s.border.top), | |
bottom: dim.s.rect.bottom - dim.s.border.bottom - dim.s.scrollbar.bottom - dim.e.rect.bottom, | |
left: dim.e.rect.left - (dim.s.rect.left + dim.s.border.left), | |
right: dim.s.rect.right - dim.s.border.right - dim.s.scrollbar.right - dim.e.rect.right | |
}; | |
var animOptions = {}; | |
// vertical scroll | |
if (options.direction.y === true) | |
{ | |
if (rel.top < 0) | |
{ | |
animOptions.scrollTop = dim.s.scroll.top + rel.top; | |
} | |
else if (rel.top > 0 && rel.bottom < 0) | |
{ | |
animOptions.scrollTop = dim.s.scroll.top + Math.min(rel.top, -rel.bottom); | |
} | |
} | |
// horizontal scroll | |
if (options.direction.x === true) | |
{ | |
if (rel.left < 0) | |
{ | |
animOptions.scrollLeft = dim.s.scroll.left + rel.left - options.margin; | |
} | |
else if (rel.left > 0 && rel.right < 0) | |
{ | |
animOptions.scrollLeft = dim.s.scroll.left + Math.min(rel.left, -rel.right) + options.margin; | |
} | |
} | |
// scroll if needed | |
if (!$.isEmptyObject(animOptions)) | |
{ | |
if (rootrx.test(scroller[0].nodeName)) | |
{ | |
scroller = $("html,body"); | |
} | |
scroller | |
.animate(animOptions, options.duration) | |
.eq(0) // we want function to be called just once (ref. "html,body") | |
.queue(function (next) { | |
$.isFunction(options.complete) && options.complete.call(scroller[0]); | |
next(); | |
}); | |
} | |
else | |
{ | |
// when there's nothing to scroll, just call the "complete" function | |
$.isFunction(options.complete) && options.complete.call(scroller[0]); | |
} | |
} | |
// return set back | |
return this; | |
} | |
}); | |
var scrollValue = { | |
auto: true, | |
scroll: true, | |
visible: false, | |
hidden: false | |
}; | |
$.extend($.expr[":"], { | |
scrollable: function (element, index, meta, stack) { | |
var direction = converter[typeof (meta[3]) === "string" && meta[3].toLowerCase()] || converter.both; | |
var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle); | |
var overflow = { | |
x: scrollValue[styles.overflowX.toLowerCase()] || false, | |
y: scrollValue[styles.overflowY.toLowerCase()] || false, | |
isRoot: rootrx.test(element.nodeName) | |
}; | |
// check if completely unscrollable (exclude HTML element because it's special) | |
if (!overflow.x && !overflow.y && !overflow.isRoot) | |
{ | |
return false; | |
} | |
var size = { | |
height: { | |
scroll: element.scrollHeight, | |
client: element.clientHeight | |
}, | |
width: { | |
scroll: element.scrollWidth, | |
client: element.clientWidth | |
}, | |
// check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars | |
scrollableX: function () { | |
return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client; | |
}, | |
scrollableY: function () { | |
return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client; | |
} | |
}; | |
return direction.y && size.scrollableY() || direction.x && size.scrollableX(); | |
} | |
}); | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment