Sometimes our pages have to deal with long(er) unpaged lists of data. Long enough to fall off the viewable browser window estate. Actually you don't even have to deal with lists at all. Let me re-define the problem: when you have a scrollable element on your page (may be the very body
of it) and you need to programmatically scroll to its out-scrolled child element, then this jQuery plugin is just for you. Now let's see when we have to do this and what could go wrong.
This plugin has a new (versioned) home on GitHub. The same code (as below) is provided there with complete version history. And you can also get a minified version over there if you're in a hurry and just need to run it in production right away. But the best thing is you can watch the project over there so you can be notified of new updates.
A real world scenario and usability issues
I have a few Ajax-powered pages in my application that display unpaged lists. Some of them may get long enough to fall off visible browser area. And all of these lists allow adding to as well. There's a nifty button Add at the top that opens a modal dialog window with an empty item editor form in it.
The process of adding a new item in the list is as follows:
- user clicks the Add button at the top;
- a nice modal dialog with an empty list item form opens;
- user fills in data which gets validated along the way;
- when all data is valid, an Ajax call is issued to save the item;
- item gets saved on the server and all I get back is HTML of the new item that I can directly append to my existing list;
- I append the item to the list and highlight it (you know the nice one-time background blink that we use to focus user's attention; usually to a new/changed element);
This seems fine, but what happens when the list falls off the viewable browser area? Well... we append the new item and highlight it. But since the item is not visible, users have all the right to become confused about what just happened. I guess that's a nice example of a bad usability.
What should be changed then? For starters we should display the item to the user before highlighting it. But that's not all. If we just jump to this item users might get confused. Again. How should they know where they jumped? If the content looks familiar, they may assume they've been moved up or down on the same page, but if the new scrolled-into content looks a bit different, they may as well think they were redirected to a different page. Another bad usability example. Not a typical thing developers would think about, but we should.
The correct solution
There are basically two things that need to be done here:
- we have to display our element before highlighting it;
- we have to smoothly scroll to it, not just jump - by smoothly I mean animate the movement so it becomes obvious whether page is scrolling up or down (or even left or right for what it's worth);
Why not reuse an existing jQuery plugin?
There's a great existing jQuery plugin scrollTo that does animated scrolling, but it requires you to provide the scrollable element that will get scrolled. In my particular case I wasn't able to use it without any additional logic because my scrollable elements are purely dynamic and I don't even know whether they exist in the first place (my application is part of a Sharepoint 2010 site which uses dynamic master pages). Hence I would either have to provide functionality to check for a scrollable ancestor element and use this existing plugin, that supports many usage scenarios but I would only use one, or write a less complex plugin that only requires you to execute it against the element you wish to display. I decided for the latter.
In order to make this fully reusable I've written a rather short jQuery plugin that does animated scrolling to any element (when scrolling is at all possible - so it has a scrollable ancestor element). This is the code:
1: /*!
2: * jQuery scrollintoview() plugin and :scrollable selector filter
3: *
4: * Version 1.8 (14 Jul 2011)
5: * Requires jQuery 1.4 or newer
6: *
7: * Copyright (c) 2011 Robert Koritnik
8: * Licensed under the terms of the MIT license
9: * http://www.opensource.org/licenses/mit-license.php
10: */
11:
12: (function ($) {
13: var converter = {
14: vertical: { x: false, y: true },
15: horizontal: { x: true, y: false },
16: both: { x: true, y: true },
17: x: { x: true, y: false },
18: y: { x: false, y: true }
19: };
20:
21: var settings = {
22: duration: "fast",
23: direction: "both"
24: };
25:
26: var rootrx = /^(?:html)$/i;
27:
28: // gets border dimensions
29: var borders = function (domElement, styles) {
30: styles = styles || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(domElement, null) : domElement.currentStyle);
31: var px = document.defaultView && document.defaultView.getComputedStyle ? true : false;
32: var b = {
33: top: (parseFloat(px ? styles.borderTopWidth : $.css(domElement, "borderTopWidth")) || 0),
34: left: (parseFloat(px ? styles.borderLeftWidth : $.css(domElement, "borderLeftWidth")) || 0),
35: bottom: (parseFloat(px ? styles.borderBottomWidth : $.css(domElement, "borderBottomWidth")) || 0),
36: right: (parseFloat(px ? styles.borderRightWidth : $.css(domElement, "borderRightWidth")) || 0)
37: };
38: return {
39: top: b.top,
40: left: b.left,
41: bottom: b.bottom,
42: right: b.right,
43: vertical: b.top + b.bottom,
44: horizontal: b.left + b.right
45: };
46: };
47:
48: var dimensions = function ($element) {
49: var win = $(window);
50: var isRoot = rootrx.test($element[0].nodeName);
51: return {
52: border: isRoot ? { top: 0, left: 0, bottom: 0, right: 0} : borders($element[0]),
53: scroll: {
54: top: (isRoot ? win : $element).scrollTop(),
55: left: (isRoot ? win : $element).scrollLeft()
56: },
57: scrollbar: {
58: right: isRoot ? 0 : $element.innerWidth() - $element[0].clientWidth,
59: bottom: isRoot ? 0 : $element.innerHeight() - $element[0].clientHeight
60: },
61: rect: (function () {
62: var r = $element[0].getBoundingClientRect();
63: return {
64: top: isRoot ? 0 : r.top,
65: left: isRoot ? 0 : r.left,
66: bottom: isRoot ? $element[0].clientHeight : r.bottom,
67: right: isRoot ? $element[0].clientWidth : r.right
68: };
69: })()
70: };
71: };
72:
73: $.fn.extend({
74: scrollintoview: function (options) {
75: /// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary>
76: /// <param name="options" type="Object">Additional options that can configure scrolling:
77: /// duration (default: "fast") - jQuery animation speed (can be a duration string or number of milliseconds)
78: /// direction (default: "both") - select possible scrollings ("vertical" or "y", "horizontal" or "x", "both")
79: /// complete (default: none) - a function to call when scrolling completes (called in context of the DOM element being scrolled)
80: /// </param>
81: /// <return type="jQuery">Returns the same jQuery set that this function was run on.</return>
82:
83: options = $.extend({}, settings, options);
84: options.direction = converter[typeof (options.direction) === "string" && options.direction.toLowerCase()] || converter.both;
85:
86: var dirStr = "";
87: if (options.direction.x === true) dirStr = "horizontal";
88: if (options.direction.y === true) dirStr = dirStr ? "both" : "vertical";
89:
90: var el = this.eq(0);
91: var scroller = el.closest(":scrollable(" + dirStr + ")");
92:
93: // check if there's anything to scroll in the first place
94: if (scroller.length > 0)
95: {
96: scroller = scroller.eq(0);
97:
98: var dim = {
99: e: dimensions(el),
100: s: dimensions(scroller)
101: };
102:
103: var rel = {
104: top: dim.e.rect.top - (dim.s.rect.top + dim.s.border.top),
105: bottom: dim.s.rect.bottom - dim.s.border.bottom - dim.s.scrollbar.bottom - dim.e.rect.bottom,
106: left: dim.e.rect.left - (dim.s.rect.left + dim.s.border.left),
107: right: dim.s.rect.right - dim.s.border.right - dim.s.scrollbar.right - dim.e.rect.right
108: };
109:
110: var animOptions = {};
111:
112: // vertical scroll
113: if (options.direction.y === true)
114: {
115: if (rel.top < 0)
116: {
117: animOptions.scrollTop = dim.s.scroll.top + rel.top;
118: }
119: else if (rel.top > 0 && rel.bottom < 0)
120: {
121: animOptions.scrollTop = dim.s.scroll.top + Math.min(rel.top, -rel.bottom);
122: }
123: }
124:
125: // horizontal scroll
126: if (options.direction.x === true)
127: {
128: if (rel.left < 0)
129: {
130: animOptions.scrollLeft = dim.s.scroll.left + rel.left;
131: }
132: else if (rel.left > 0 && rel.right < 0)
133: {
134: animOptions.scrollLeft = dim.s.scroll.left + Math.min(rel.left, -rel.right);
135: }
136: }
137:
138: // scroll if needed
139: if (!$.isEmptyObject(animOptions))
140: {
141: if (rootrx.test(scroller[0].nodeName))
142: {
143: scroller = $("html,body");
144: }
145: scroller
146: .animate(animOptions, options.duration)
147: .eq(0) // we want function to be called just once (ref. "html,body")
148: .queue(function (next) {
149: $.isFunction(options.complete) && options.complete.call(scroller[0]);
150: next();
151: });
152: }
153: else
154: {
155: // when there's nothing to scroll, just call the "complete" function
156: $.isFunction(options.complete) && options.complete.call(scroller[0]);
157: }
158: }
159:
160: // return set back
161: return this;
162: }
163: });
164:
165: var scrollValue = {
166: auto: true,
167: scroll: true,
168: visible: false,
169: hidden: false
170: };
171:
172: $.extend($.expr[":"], {
173: scrollable: function (element, index, meta, stack) {
174: var direction = converter[typeof (meta[3]) === "string" && meta[3].toLowerCase()] || converter.both;
175: var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle);
176: var overflow = {
177: x: scrollValue[styles.overflowX.toLowerCase()] || false,
178: y: scrollValue[styles.overflowY.toLowerCase()] || false,
179: isRoot: rootrx.test(element.nodeName)
180: };
181:
182: // check if completely unscrollable (exclude HTML element because it's special)
183: if (!overflow.x && !overflow.y && !overflow.isRoot)
184: {
185: return false;
186: }
187:
188: var size = {
189: height: {
190: scroll: element.scrollHeight,
191: client: element.clientHeight
192: },
193: width: {
194: scroll: element.scrollWidth,
195: client: element.clientWidth
196: },
197: // check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars
198: scrollableX: function () {
199: return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client;
200: },
201: scrollableY: function () {
202: return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client;
203: }
204: };
205: return direction.y && size.scrollableY() || direction.x && size.scrollableX();
206: }
207: });
208: })(jQuery);
How it works
First of all this plugin was written to support all three major browsers: Internet Explorer, Firefox and Chrome (or even Safari for that matter, because they are both Webkit browsers). We call this plugin on an element that we'd like to scroll to (or in other words, that we'd like our user to see). As you can see from the Visual Studio Javascript intellisense XML documentation this plugin can be used with an optional options
parameter that can define:
- animation duration/speed (understands same values as jQuery animate function);
- scrolling direction defined as a string of either
vertical
,horizontal
orboth
; the first two have a shorthand version withx
andy
values as well; - complete function that will be called when scrolling finishes; this function will be called in the context of the scrolled DOM element (meaning that
this
will refer to scrolled DOM element);
These two calls represent possible plugin usage:
$("tr:eq(10)").scrollintoview();
or
$("h4:last").scrollintoview({ duration: "slow", direction: "y", complete: function(){ alert("Done"); } });
When jQuery selector returns a set of multiple elements, this plugin will display the first element in this set. What it actually does is that it tries to find element's nearest scrollable (see below for the additional gem) ancestor element and scrolls it so the element gets displayed within viewable area. Complex scenarios with nested scrollable elements aren't supported because you're probably already hitting usability issues with your user interface.
Additional gem
If you looked closely there's one more thing included in this plugin. And that's the :scrollable
jQuery selector that makes it possible to find all scrollable elements. It has two usage scenarios:
- parameterless as in
$(":scrollable")
which would return all elements on the page that are scrollable (that can scroll more content than they display) in either direction; - parametrised as in
$(":scrollable(vertical)")
which would do a similar thing except that it will only look for vertically scrollable elements (can be horizontally scrollable as well but selector will ignore this fact);
Selector parameter as seen in the second example can be any of the following self explanatory values (similar to plugin's except they're not specified as strings):
horizontal
orx
vertical
ory
both
Issues/questions/suggestions/ideas?
If you need any further assistance regarding this plugin or selector, let me know. Write a comment. If you intend to use this plugin you're also welcome to provide some information how it turned out for you. I'd be happy if it helps you with your projects. Happy coding fellows.
What is the license of this plugin? :)
ReplyDeleteLines 8-9 of plugin:
Delete"Licensed under the terms of the MIT license http://www.opensource.org/licenses/mit-license.php"
@joril: As you may see I've updated plugin source that includes license reference in the top comment section. Refer to MIT license link for further information of this plugin usage.
ReplyDeleteLooks really good, but where is the download link and why cant I copy+paste from the code display? Because the linenumbers are copied as well! So basically I cannot use even if I really want to.
ReplyDelete@Anonymous: You're right. There's no link to copy the code in one go. I mean code only.
ReplyDeleteI don't know if you're aware of this, but multi-line editing is available in many text editors (including Visual Studio and Notepad++). If you hold ALT key and drag your mouse vertically along the same column (so not selecting any text), you will en up with a multi-line cursor and you can edit all these lines at once.
So. Steps:
1. Copy the code from my blog.
2. Paste it in either Visual Studio or Notepad++ or similar
3. Hold ALT and drag a cursor at the beginning of all lines.
4. press DELETE key as many times as needed to remove all line numbers.
That's it. It's a matter of seconds really. If you'd like you can also remove every second line, because it copies it as empty line.
Would this help?
@Anonymous: I've added a link at the top of each code block that toggles line numbers so it should be easier to copy code from my blog posts.
ReplyDeleteHope this helps.
Yes, that definitely helped, I didn't know the alt+drag trick. Im sure Im not the only one though, so I believe you have now taken all steps necessary. So now. Right now. Im trying the plugin.
ReplyDelete@Anonymous: Glad I could help. You're welcome to tell me how it worked. I'm using it on my Sharepoint 2010 website and it works as expected. But it's true that Sharepoint 2010 default master doesn't use scrollable document/window but rather scrollable container that keeps title bar always on top.
ReplyDeleteAs I anticipated, it worked like a charm! I was using the native jscript scrollintoview() but it made my whole page jump in IE and FF. Now everything is melted chocolate on icecream.
ReplyDeleteI'm a newbie with JQuery Plugins, please could you instruct me how can i use this code? I already copied it as a new file and referenced it in my Master Page as Javascript file (as did with jquery itself), but when I invocate scrollintoview it says method invalid. Please Help me.
ReplyDelete@Anonymous: Example usage is described in short in How it works section of this blog post. If you tried executing it like:
ReplyDelete$(/* some selector expression */).scrollintoview();
and it didn't work, then I suggest you check your code with Firebug and see how files get loaded and of course when do you execute your code. Is it on page ready or while loading or something similar. Try to analyse your code and what executes when. Firebug will be very helpful.
Can you use Jquery at all (if you've loaded it in the same way)?
The most I can help you is to click this Google search query and read about jQuery integration with asp.net WebForms (if that's what you're using)
Any reason why this wouldn't work?
ReplyDelete$('.contact').click(function() {
$('#contact').slideToggle('slow', function() {
$('#contact').scrollintoview();
});
return false;
});
@Anonymous: "...wouldn't work" as what? Can you elaborate a bit more on this?
ReplyDeleteWhat seems to not work as expected?
1. You don't see sliding effect that you run first?
2. It doesn't scroll into view afterwards?
3. You get an error?
4. Not working in a particular browser?
5. Which element is scrollable in your case? Document itself or some arbitrary sub element? Maybe an IFRAME or element in its particular document?
So many questions that need answers. :)
@Anonymous: If you were trying to scroll your whole window/document I understand you had problems. I've changed the code so it should work even in this case as expected and in all major browsers (IE, FF and CH).
ReplyDeleteJust make sure you're using plugin version 1.5 (11th March 2011).
But if you had any other issues you'll have to provide more info.
This is great!
ReplyDeleteHow do I have it scroll to the selected tr with a class "selectedTR" in a div with overflow and have it put the selected item at the top of the div with the overflow set. It scrolls to it but puts it at the bottom of the div with the overflow.
Hope that makes sense. :)
Thanks,
Aaron
@Aaron: The purpose of this plugin is to scroll a certain element into view (as close as possible) and not to scroll it to the top of the scrollable element (it would be called scrolltotop then). Hence below the view elements will be scrolled to the bottom of scrollable ancestor, and elements above the view will be scrolled to the top of the scrollable ancestor. And those anywhere in the view won't scroll at all.
ReplyDeleteMaybe you should consider highlighting the element as well. In this case it doesn't really matter where scrolling positions it because you will visually highlight it so it will be apparent which element you're trying your user to focus on.
The other problem with scrolling to top is that it's not consistent. When scrollable ancestor can't be scrolled that much your element will still be displayed at the bottom (if it's the last visual child of the scrollable ancestor for instance). Highlighting is therefore still important.
If none of these arguments holds water for your case I suppose it's probably better that you use a different jQuery plugin that allows scrolling as per your requirements. scrollTo() plugin.
http://flesler.blogspot.com/2007/10/jqueryscrollto.html
and his demos:
http://demos.flesler.com/jquery/scrollTo/
In my case I wasn't able to use this particular plugin because my scrollable element is not known during development hence my plugin automatically finds the closest scrollable ancestor. Just to clear things a bit.
Hope this helps...
Hey Robert thanks for the reply and makes perfect sense!
ReplyDeleteThanks so much!
Works great. However, I did have to hack the code a bit so that the complete function is always called. I display a toolbar after I scroll the element into view and I have to wait for the scrolling to complete before positioning the toolbar so I show the toolbar in the complete function, but the complete function wasn't being called when there was nothing to scroll.
ReplyDelete@Anonymous: You're completely correct about the complete() function. It should be called regardless of scrolling. And since it's called in context of the scrollable element it won't be called when things can't be scrolled in the first place.
ReplyDeleteCall to complete() will therefore execute only when there's a scrollable ancestor (otherwise we won't have context of the call) and after the element is in view (whether after scrolling to it or immediately).
Thanks for pointing out this glitch.
offset?
ReplyDelete@Luke: Can you elaborate on your offset?
ReplyDeleteI'm guessing Luke means a situation where you toggle a DIV, then you want it to scroll past the bottom of that DIV by X number of pixels. I could use such a function, but was able to fake it by adding some extra padding in the CSS.
ReplyDelete@Eric: If that's what he was after (I though he was asking about providing offset settings in any direction, but wasn't really sure) then your hack is probably a nice simple and more predictable solution because you can always assure that certain element will be able to be scrolled a bit higher. The problem would be with the very last element that can in that situation hit the scroll limit. That by itself is a bad user experience to me because users can't always expect the element at the very same position. And that's confusing and non-uniform.
ReplyDeleteEven though it would still be possible to implement but would also be a bit tricky what to do when element is visible but offset from visible view port is a bit smaller than the offset provided along with the call?
Basically this is not so much a problem of code but rather experience and predictability.
Hey Robert,
ReplyDeleteThanks for posting this, it might just be what I am looking for!
I have used the script to create a .js file and called it 'jqueryscroll.js', which gets called into the head (under the jQuery 1.4+ call).
I then have this action in the footer:
jQuery(document).ready(function() {
jQuery('#').scrollintoview();
});
I must be missing something though as when I try to get the script to fire using a conventional anchor within the body of the doc, nothing happens. Have I missed something entirely?
Cheers and thanks in advance.
Gerard
@Gerard:: The problem is that you haven't used a valid jQuery selector to select an element on your page. $("#") barely throws a syntax error because jQuery doesn't recognise this selector.
ReplyDeleteYou should therefore use a valid selector that jQuery can return an element (or a set of them):
$("some_valid_selector").scrollintoview();
and everything should work as expected.
I can't thank you enough.
ReplyDeleteYou turned a productive workday into an day with an enjoyable progress. It worked, but it had rough edges. You made it smooth.
Lots of kudos from Hamburg/Germany!
Works for me in IE 7 but not in FF 5.0. Any thoughts?
ReplyDelete@Erik Eckhardt: I'm also using the latest FF5 (says there're no newer updates) and it works fine. It's true I'm using jQuery 1.4.4 but functionality that's used within plugin hasn't changed in later jQuery versions. Without providing any insight into your code It's hard to tell why things aren't working for you...
ReplyDeleteMaybe you're using selectors that don't return elements anymore or something. Hard to tell.
Great plugin, I'm using it for my Chrome extension for Google+: http://huyz.us/google-plus-me/
ReplyDelete@huyz: Thanks for letting me know. This is really great news. I'm glad you're making such a good use of it. And this way it will gain some more users as well. :)
ReplyDeleteHow about using any easing? Would love to smooth out the transitions.
ReplyDelete@Anonymous: Good suggestion. Will include that.
ReplyDeleteSimple comment system with form submission via ajax. Comment html is built and displayed at the bottom of existing comments in a div #mainResponse.
ReplyDeletesuccess: function(responseText){
if (responseText.indexOf("Notice:") !== 0) {
$.unblockUI();
$('#mainForm').toggle();
$("#mainResponse").html(responseText);
$("#mainResponse").scrollintoview({ duration: 2500, direction: "vertical" });
} else {
$("div.blockMsg").text(responseText);
setTimeout($.unblockUI, 1500);
}
}
Am I doing this wrong? Seems pretty self explanatory but I'm not getting any scroll at all. I'm using Jquery 1.4.2, FireFox 6.0.
@Anonymous: I'm sorry for such a long response delay that's due to my past vacation.
ReplyDeleteApart from your code not being optimised it seems you're doing it all right. If your #mainResponse div isn't visible it should be scrolled to when ancestor is scrollable (with scrollbars).
Could you provide a JSFiddle of your problematic code so I could take a look?
Hi Robert, I found your plugin most useful, especially as it handles both scrolling at the document level and inside an arbitrary element. But what if you want to scroll multiple elements into view? I found out your plugin could easily be modified to handle this case, by just modifying the dimensions() function and adding one small extra function. See new code here: http://regularwebmaster.blogspot.com/2011/11/scrollintoview-revised.html and please feel free to do what you want with it.
ReplyDelete@Martin Boström: I'm glad you found my plugin useful and extended it even further to your needs. That's great, but I think your use scenario is rather seldom situation hence I decided to keep my plugin as is.
ReplyDeleteLet me explain a bit why. If my plugin would work with several elements it would be totally unreliable. Your code completely relies on you to provide elements that are within a common ancestor. A general plugin should be more robust that that.
Anyway. Thanks again and I hope you keep your initial satisfaction.
Awesome plugin!
ReplyDeleteTried a ton of snippets from Stackoverflow but none would work correctly in my case.
Just dropped yours in and it works out of the box like a champ.
Thanks a lot, Robert!
@Anonymous: Thank you so very much! I'm always glad to get such response from other fellow developers.
ReplyDeleteKeep following my blog and maybe there'll be other useful things as well...
Absolutely. :)
ReplyDeleteHi, can you help me make the alignment on the top not bottom? Like the native version of scrollIntoView.
ReplyDelete@Luke: my plugin's functionality doesn't support scrolling elements to top. It rather scrolls either to top or bottom depending where the element is before you want it to be visible.
ReplyDeleteFor always scrolling elements to top (which is unreliable thus not being part of my plugin) I suggest you rather use Ariel Fleisler's scrollTo plugin or use jQery's scrollTop function (along with animate).
Great plugin, just what I was looking for ;)
ReplyDeleteExcept for one thing - it would be indeed nice to have an offset/margin setting. I read the earlier comment about padding "hack", but unfortunately that's not a solution for me. In my situation I have a user bar position: fixed at the top of the browser window (just like facebook does, or a lot of other websites these days). And I also have a vertical list of items on which you can drop other elements. When you drop something on that list element its position may change (they're always sorted) and go out of view. I want to scroll to that new position, but the list element ends up hidden behind the top user bar. A few pixels of top offset would fix all my problems :P
thank you for sharing such an informative blog.. it is very valuable blog..it will be helpful in my business.. Air Conditioning Baltimore .. we offer excellent air conditioning services in Baltimore..
ReplyDeleteHi Robert nice article - a colleague of mine implemented this a few months back (now converted to coffee script) for a photo gallery. I now need to change the image gallery slightly so that the scroll links only appear when they are needed i.e. if there are only a few items in the gallery no scroll links appear as they are not needed. I thought this was a standard option with the scrollintoview() plugin but I can't find any documentation on how to do this - do you know of any resources that explain how this can be done? Many thanks.
ReplyDeleteHi. Along with my plugin there's also a selector filter :scrollable that you can use in your case.
DeleteWhen you page loads (or when you load the gallery images) you should check if your gallery container is scrollable and display those links only when it is:
$("#yourCcntainerId:scrollable").length && $(".galleryLinks").show();
This should do the trick for you.
Your plugin is really great, it saves my life!
ReplyDeleteIt appears to be broken with jQuery 1.8. :(
ReplyDeleteUncaught TypeError: Cannot read property '3' of undefined jquery.scrollintoview.min.js:174
I created a ticket: https://github.com/litera/jquery-scrollintoview/issues/10
DeleteMore info: http://bugs.jquery.com/ticket/12201
Thanks for this Yann. I'll look into it and see what may be wrong.
DeleteNice plugin.
ReplyDeleteWorked when the jQuery scrollIntoView plugin did not.
Please fix for use with jQuery 1.8.x
This has been on ma priority list now for 2 weeks. Code will be updated to support jQuery latest version.
DeleteThank you.
Hi.
ReplyDeleteMore specifically, the problem comes from the fact that your :scrollable pseudo-class filter has the signature "function (element, index, meta, stack)" and jQuery (1.8) wants a "function (element, context)" (from inside the matcherFromGroupMatchers function).
It's easy to patch your sources for specific needs, bypassing the use of :scrollable, so many thanks anyway!
Regards,
Claude
Hello Robert,
ReplyDeleteThanks that useful plugin..
My problem: i want for object's closest first scrollable parent. but page(iframe) scrollbar move :(
Plugin is designed to find closest scrollable ancestor that also has excess content (so it's actually phisically scrollable). Some element may have scrollbars but not enough content to actually be scrollable. Such elements are skipped and plugin progresses further up DOM tree.
DeleteI modified your code to run the "complete" function even if there was no scrollable element found. Thanks for this useful jQuery plugin.
ReplyDeleteI also like only one var declaration per function, so I changed that, too. And one more tweak you may or may not like:
dirStr = [
"",
"horizontal",
"vertical",
"both"
][options.direction.x * 1 + options.direction.y * 2];
I sure wish I could style that code a bit...
Hi Robert,
ReplyDeleteI have implemented this scrollintoview function on my application. It works in FF 13, Chrome 25, but not in IE9. In IE9, it simply wouldn't scroll into the element at all. I'm using JQuery 1.7.1. My code looks like this:
jQuery(qaNode).scrollintoview({
duration: 600,
direction: "both",
complete: function() {
dojox.fx.highlight({
color: "#E8F6FB",
node: targetNode1,
duration: 600
}).play(250);
}
});
Many thanks.
Works great!
ReplyDeleteOne problem: With no explicit DOCTYPE it does nothing at all.
No error reported, but no scroll happens.
If I add the DOCTYPE from the demo.html, it works fine.
If I add a DOCTYPE of simply <!DOCTYPE html>, it works fine.
I have a large app mostly finished and had never specified a DOCTYPE,
so I'm leery of adding one now, unless I take the time to re-test
all functionality on all device/OS/browser combinations.
Any way to get it to work w/o adding a DOCTYPE?
Thanks!
--Fred
This is great!
ReplyDeleteAny word on incorporating easing?
Cheers
Easy. Don't use my plugin and just use browser built-in functionality.
ReplyDeleteIs there an easy way to update this plugin to bring the scrolled-to element to the center of the viewport / parent? Instead of simply bringing it "within view"? That would be great from a focusing perspective when using for both x and y.
ReplyDeleteThere're problems related to that. Let me point out two perfectly viable extremes: elements that are rendered at the top of the page that fill up to top half of the screen and elements that are rendered at the bottom of the page that fill the bottom half of the screen when page is completely scrolled down. All of these elements would fail to scroll into the middle of the screen. This would make this plugin inconsistent and such flaw would be regarded as a bug.
DeleteThe best thing you can do is to keep elements get scrolled into view and when scrolling finishes, you visually highlight them in whatever way seems appropriate.
Hi Robert....
ReplyDeleteThis looks exactly what I want but I cannot get it to work...
Simple HTML is
DIV
TABLE
TR
TD
TD
TD
TD
TD ID="xx"
The "xx" TD is hidden by "overflow-x: hidden" on the DIV and want to nicely scroll in... stepping your code gets to "if (scroller.length > 0) {" but .length = 0 so not executing
Thanks
Can you plese create a jsfilddle (or similar) so it will be easier to analyse?
DeleteGreat solution! Works like a charm. I'm using jQuery 1.11.1 on latest Chrome, FF and IE. I tested scrolling table row into view.
ReplyDeleteOnly issue I have right now is the speed, I would like to speed it up somehow. Right now is user holds keyup/down there is some serious lagging after scrolling a couple of rows. I tried:
$("#" + rowid).scrollintoview({ duration: "fast" });
but that seems to be default.
I'll take a look at you code to see if I can speed it up by adding an option or animating faster?
If you know how, let me know, else I'll give it a go.
This is very cool indeed.
Hans
$("#" + rowid).scrollintoview({ duration: 10 });
ReplyDeletewill scroll very fast. :-)
I should have checked the jQuery documentation before posting.
Thanks again.
For the same purpose I added a direct link to it. And as I see you've found it yourself already.
DeleteSorry I'm very new to web dev, here is the site I'm working on: http://nwcdnetwork.businesscatalyst.com/ .I wanted to add a stop() or delay() method to scrollintoview so when the user scrolls over and out of states it doesn't cause the list to erratically move up and down till the user clicks on a state or stops moving long enough for the animation que to catch up. Some of the things I've tried are commented out in the main.js file around line 80. Any help is appreciated.
ReplyDeleteI'm sorry for seeing this so late, but your website isn't available any more. If you point me to a different URL I'll take a look at it and see what you're trying to do. That is if you're still having issues of course.
DeleteAny chance to get this plugin posted on Bower repo? Would be very nice!
ReplyDeleteGreat work man (y).
ReplyDeleteWorks like a charm, and has saved me some precious coding time. :) Thanks for publishing and writing the blog post.
ReplyDeleteThanks for sharing this!
ReplyDeleteUnfortunately, the scroll of a root element (html/body/document) does not work any more, once the page has been scrolled down a bit. See https://codepen.io/leanix/pen/XqXeOd. For some reason, your plugin cancels the scroll animation assuming that user has initiated a scroll himself.