I have a slider a little like this:
________
------------| |---------------------------------------
: <-- | slider | --> : : : :
------------|________|---------------------------------------
I'm using jQuery UI to make the slider draggable. This works fine.
The problem is that I'm executing a function each time the slider crosses a section boundary (indicated by :). This function takes approximately 30ms to execute (it's constructing a DocumentFragment and inserting it into the document). During this time the browser does not respond to events, so jQuery UI stops adjusting the slider's position and the drag is prematurely ended (the user is forced to release the mouse button and commence a new drag). This makes the slider feel jerky and buggy.
Is there a solution to this problem?
Edit: I've created a simple fiddle which demonstrates the problem. Note that while the drag is essentially paused during the execution of the slow function, it resumes afterwards. This makes me suspect that there's something about my more complex scenario which is causing the drag event to be cut short (perhaps the DOM manipulation is causing the position of the slider to change briefly).
Edit: After further investigation I'm much closer to understanding the problem. The DOM manipulation code contains a line something like $('.droppable').remove(). This removes the matching elements from the DOM after detaching any event handlers bound to them and removing any associated data. If I use .detach() instead, the behaviour is the same as in the simple fiddle above. Something untoward is happening during the cleanup.
I'd suggest to use jQuery's or Underscore's debounce function, or some equivalent method, to call the function just once every 100 or 200 ms or so.
This won't make things smooth, but at least they won't feel buggy.
Interesting article about it.
Related
I have a Javascript function called updatescreen() that performs various actions on page elements.
I call updatescreen() on page load, page scroll, touchmove, orientation and resize. Basically, any time the user is moving things around, I need to update elements to make the page look and function correctly.
Not every trigger actually modifies a page element, but I have to do some checking.
Everything works well, until I start scrolling (or flicking on an iPhone) very quickly. One trigger that happens is that I need to change the position of an element's "top" value (CSS). I have tried this with jQuery "animate" as well as just "css".
If I scroll slowly enough, everything is fine. If I scroll too fast, the script doesn't seem to keep up with my CSS/animation changes, and it ignores the last few, leaving the element in the wrong position. At least, this is what I think is happening.
Is it correct that firing different CSS values at the same element, too quickly, would cause the last ones to be ignored? Are there rules with regards to how jQuery handles this?
Any suggestions on how I could improve this entire workflow?
Thanks!
I'm currently making a little navigation menu. In one of those dropdown menus, I need one category to expand. It works. But, when category retracts with the help of jQuery .hide('slow'), the menu I want to hide blinks once at the end of the hide animation, and then is properly hidden.
Here's some code below to illustrate my problem :
$(document).ready(function(){
var verif = false;
$("a").hover(function() {
var texte = $(this).text();
var tab = new Array;
tab = texte.split(" ");
if (tab[0] === "+ Deroule" && verif === false) {
$("#submenu").show("slow");
verif = true;
}
else if (tab[0] === "+ Deroule" && verif === true) {
$("#submenu").hide(8000);
//There, I can clearly see, at the end of the hide animation, the previously opened content blinking once before it hides completely.
verif = false;
}
});
});
Without seeing your HTML/CSS it's hard to actually pin the problem down - hint hint create a fiddle hint hint :) - but the first thing that I would do, if I were you, is change the .hover() method to .mouseenter() and .mouseleave() (NOT .mousein() and .mouseout() - that's another conversation about IE support - you can/should look that up on jQuery's site, if you're not familiar).
Why do this? The .hover() method is actually just shorthand for .mouseenter()/.mouseleave() but, for various reasons (including the fact that it is not as widely supported as the two full methods), it occasionally fails to work properly. I cannot guarantee that this is the problem in this situation, but, especially since this appears to be a browser issue, it is always best to go back to basics and start with as much support as possible.
EDIT:
After seeing your simplified fiddle, I think I have a better understanding of what your issue is and what you're trying to do. I have had the same issue before and, from my experience, I can tell you that it is actually a difficult issue to account for.
Elements jump when they are shown/hidden using the .show(), .hide(), slide, and fade methods because as soon as the animation completes they are removed from the flow (in most browsers - not chrome - the show/hide methods do not cause the element to shrink all the way to nothing, so there is still choppiness). Here are two of my most recent solutions to this issue:
In one situation, I was tasked with creating view-more and view-less buttons inside a menu, which would make elements appear/disappear SMOOTHLY one at a time. If you simply try to call a show/hide, slide, or fade method on the collection to be shown, they will all animate at the same time, and the same thing happens when trying to make them run on callback. Additionally, sliding methods wouldn't work because this animation was going to be called on multiple items at a time, thus causing a lot of overhead and making the effect choppy in slower browsers (and even a bit in FF). The show/hide methods worked in some browsers, as they actually do reduce the size of the element, but they are choppy in others so they were out (effectively, their animations don't "complete" in all browsers - see above). Thus, I was left with three options: use fade, animate, or a combination of both.
Solution 1:
The original plan, as it was (with the given specs) the best solution at the time, was to use the animate method to simply slide a wrapper div (with overflow set to hidden up or down, based on the offset of a target element in the list). This worked great, but I was then given the requirement of having each element disappear in succession, via a fade effect. If you do choose this method, then I suggest that you instead use slide methods - as you are not attempting to show view more/less links, this shouldn't be a problem (yes, I know that this probably doesn't sound like it would be an issue for view more/less links, but the way I had to implement it, slides actually did cause an issue).
Solution 2:
Using fade on its own was an issue because everything was still going to fade at the same time and, even if they didn't, the animation would still jump once the fade finished. Thus, the first issue I needed to solve was the animations running at the same time. Using a timer was out of the question because of the overhead, so I wrote a function that recursively called the fade on each element in the collection after calculating the necessary delay between calls (based on a supplied speed argument, and the number of elements to be faded). Next, I still had to solve the jumping issue once the animations were complete. To fix this, I implemented the .fadeTo() method, thus keeping the elements in the flow of the document after their animations. This did, however, cause two more issues:
The .fadeTo() method is not supported in IE (it may be in 9 and 10 - I can't remember)
After the animation, there was a lot of empty space due to the elements not being removed from the flow
To remedy the first issue, I added a browser-specific method for IE, which set the display property of each element to block and their visibility to hidden once their fade-outs were completed - it also did the reverse just before their fade-ins were started.
I solved the second issue by using the animation method that I described previously. To make this work, I had to write another auxiliary method that calculated the speed at which the container needed to slide, based on a given speed of the animation and the number of elements being shown/hidden.
I know that this was a lot of detail, but I hope it helps you determine how best to go about this.
Let me know if you have any questions or need any clarification when you move forward with your implementation. Good luck! :)
I am building a little web based application using an HTML table. One unusual property of this table is that it has fixed top row and left column (similar to excel). I accomplished this using a little jQuery and CSS.
The problem is, the jQuery event that triggers my code is the $(window).scroll event, and evidently most browsers (Chrome and IE) redraw the page before the code that is called by this event is finished running. As a result, the left column and top row take a split second to "catch up" to the rest of the table.
I've provided a stripped down jsFiddle example in order to show you my problem. Note: The lag isn't very noticeable when the table is so small and (relatively speaking) empty of content. Nevertheless, it is there (assuming you aren't using firefox). Is there any way to get rid of this lag?
Thanks!
Just an idea, but maybe worth considering: You could try using requestAnimationFrame as described in this article (or in a similar way). That might smooth out the update/"lag" issues.
I didn't get this to work with either the scroll event or with requestAnimationFrame. What I ended up doing is attaching to the mousewheel event, which fires before scrolling and rendering has happend. If the scroll event is going downward, I can apply the appropriate class in time for rendering.
When running a Jquery animation like slideDown(), it looks like a number of element-specific css properties is set to be updated at a specific interval and when the animation is complete these properties are unset and the display property is simply set to auto or whatever. At least in firebug you can't see those temporary properties any more.
The problem I've encountered is the scenario where we stop the slide down with stop(). The element is then left with the current temporary css values. Which is fine because it has to, but let us say that I stoped the slidedown because I have decided to slide it back up again a bit prematurely. It would look something like this:
$(this).slideDown(2000)
//The below events is not in queue but will rather start execute almost
simultaneously as the above line. (dont remember the exact syntax)
$(this).delay(1000).stop().slideUp(2000)
The above code might not make much sense, but the point is:
After 1 second of sliding down the animation is stopped and it starts to slide back up. Works like a charm.
BUT!!! And here is the problem. Once it it has slid back up the elements css properties are reset to the exact values it had 1000ms into the slideDown() animation (when stop() was called). If we now try to run the following:
$(this).slideDown(2000)
It will slide down to the very point the prior slideDown was aborted and not further at half the speed (since it uses the same time for approximately half the height). This is because the css properties were saved as I see it. But it is not especially wished for. Of course I want it to slide all the way down this time.
Due to UI interaction that is hard to predict everything might soon break. The longer animations we use increases the risk of something like this happening.
Is this to be considered a bug, or am I doing something wrong? Or maybe it's just a feature that is not supported?
I guess I can use a callback function to reset the css properties, but depending on the animation used, different css properties are used to render it, and covering your back would result in quite a not-so-fancy solution.
You could try to replace the slideUp and slideDown with animate.
http://api.jquery.com/animate/
This way, you are explicitly telling it what to do.
This is the expected (though not desirable) behavior...to get the .slideDown() to go to the full height, start the slides from the finished position, by using .stop(true, true), so the animation completes. The second true argument, telling it to skip to the end of the animation is the important part here, so those "final" values it slides back to are the full height, etc...rather than the height it was at when it stopped sliding.
This was a bug that has been fixed in jQuery 1.7.2: http://bugs.jquery.com/ticket/8685
I have the following working code to make a element of my scrollbox visible:
var next = elements.item(i+1);
var xpcomInterface = scroll.boxObject.QueryInterface(
Components.interfaces.nsIScrollBoxObject);
xpcomInterface.ensureElementIsVisible(elements);
But I would like to make a smooth-scroll (slow or not). Any idea how to do it?
update
By the way, it's for Mozilla environment.
The simplest way would be to just use a setTimeout function and just keep moving the top and left value on the element's div by a small number, until you get where you want to be.
You may want to experiment with how fast to make it move, as there is a trade-off with smooth, and the fact that it should reach the endpoint in some reasonable time.
Update:
I forgot, you will want to keep calling the setTimeout until you reach the final destination, otherwise it won't redraw the browser window.
I don't know exactly how to do it, but here's a Chrome Extension called "Smooth Scroll" where you can potentially pick through the code to see what they do and maybe get you in the right direction.
PS I love this extension.
jQuery .animate() with accelerated motion can be kind of smooth (see demo for animate with a relative move). And with a plug-in, there are other easing equations it can use.