api.scrollToY function is running recursively in scroll event only , is there any way to stop it?
$('.scroll-pane').bind('scroll',function (e) {
api.scrollToY(100)
return false;
});
I don't understand, but if you need to listen an event like scroll or resize (that fires tons of times) you can use a debouncer for your function. Underscore library have one very useful. See more:
http://davidwalsh.name/javascript-debounce-function
You can modify easy to change resize to scroll
var myEfficientFn = debounce(function() {
// All the taxing stuff you do
}, 250);
window.addEventListener('scroll', myEfficientFn);
Related
I have two event handlers defined:
window.onresize = repositionElements;
scrollElement.onscroll = handleScrollBusiness;
Now, when I resize the window, I do not want my onscroll event to fire (which it ususally does during resizing). So I thought I temporarily set some isResizing variable to true and only set it back to false after a timeout. In the onscroll function I would then first of all check, if isResizing is false and only then proceed.
However, now during testing this, I realized that when I start to resize the window, it both fires a scroll and resize event, but it fires the onscroll event first, so my onresize has no chance to disable the scroll function, since it only gets fired after the scroll function has started to execute.
Is there any way around this? Can I globally change the order of these two events? If not, what other way would there to disable the onscroll event immediately once I start resizing?
I am looking for an answer in vanilla JavaScript. Also I am new to this, so if I have some logical flaws in my way to approach this, please let me know.
Thank you!
Since you haven't posted an example code of your problem, I'm not sure if what I'm about to write helps you or not.
You could try to defer the execution of onscroll handler by wrapping it's code inside a zero-length timeout, which effectively moves the execution of that piece of code to the end of current execution stack.
Here's an example:
window.onscroll = function () {
setTimeout(function () {
// your code here...
}, 0);
}
window.onresize = function () {
// your code here...
// should be executed before onscroll handler
}
You can use RxJs Observable for this:
var resizeOb$ = Rx.Observable.fromEvent(window, 'resize');
var scrolOb$ = Rx.Observable.fromEvent(window, 'scroll')
.takeUntil(resizeOb$);
scrolOb$.subscribe(function(event){
/* handle scroll event here */
});
resizeOb$.subscribe(function(event){
/* handle resize event here */
});
This will efficiently handle the situation for you.
I am working on some javascript UI, and using a lot of touch events like 'touchend' for improved response on touch devices. However, there are some logical issues which are bugging me ...
I have seen that many developers mingle 'touchend' and 'click' in the same event. In many cases it will not hurt, but essentially the function would fire twice on touch devices:
button.on('click touchend', function(event) {
// this fires twice on touch devices
});
It has been suggested that one could detect touch capability, and set the event appropriately for example:
var myEvent = ('ontouchstart' in document.documentElement) ? 'touchend' : 'click';
button.on(myEvent, function(event) {
// this fires only once regardless of device
});
The problem with the above, is that it will break on devices that support both touch and mouse. If the user is currently using mouse on a dual-input device, the 'click' will not fire because only 'touchend' is assigned to the button.
Another solution is to detect the device (e.g. "iOS") and assign an event based on that:
Click event called twice on touchend in iPad.
Of course, the solution in the link above is only for iOS (not Android or other devices), and seems more like a "hack" to solve something quite elementary.
Another solution would be to detect mouse-motion, and combine it with touch-capability to figure out if the user is on mouse or touch. Problem of course being that the user might not be moving the mouse from when you want to detect it ...
The most reliable solution I can think of, is to use a simple debounce function to simply make sure the function only triggers once within a short interval (for example 100ms):
button.on('click touchend', $.debounce(100, function(event) {
// this fires only once on all devices
}));
Am I missing something, or does anyone have any better suggestions?
Edit: I found this link after my post, which suggests a similar solution as the above:
How to bind 'touchstart' and 'click' events but not respond to both?
After a day of research, I figured the best solution is to just stick to click and use https://github.com/ftlabs/fastclick to remove the touch delay. I am not 100% sure this is as efficient as touchend, but not far from at least.
I did figure out a way to disable triggering events twice on touch by using stopPropagation and preventDefault, but this is dodgy as it could interfere with other touch gestures depending on the element where it is applied:
button.on('touchend click', function(event) {
event.stopPropagation();
event.preventDefault();
// this fires once on all devices
});
I was in fact looking for a solution to combine touchstart on some UI elements, but I can't see how that can be combined with click other than the solution above.
This question is answered but maybe needs to be updated.
According to a notice from Google, there will be no 300-350ms delay any more if we include the line below in the <head> element.
<meta name="viewport" content="width=device-width">
That's it! And there will be no difference between click and touch event anymore!
Yes disabling double-tap zoom (and hence the click delay) is usually the best option. And we finally have good advice for doing this that will soon work on all browsers.
If, for some reason, you don't want to do that. You can also use UIEvent.sourceCapabilities.firesTouchEvents to explicitly ignore the redundant click. The polyfill for this does something similar to your debouncing code.
Hello you can implement the following way.
function eventHandler(event, selector) {
event.stopPropagation(); // Stop event bubbling.
event.preventDefault(); // Prevent default behaviour
if (event.type === 'touchend') selector.off('click'); // If event type was touch turn off clicks to prevent phantom clicks.
}
// Implement
$('.class').on('touchend click', function(event) {
eventHandler(event, $(this)); // Handle the event.
// Do somethings...
});
Your debounce function will delay handling of every click for 100 ms:
button.on('click touchend', $.debounce(100, function(event) {
// this is delayed a minimum of 100 ms
}));
Instead, I created a cancelDuplicates function that fires right away, but any subsequent calls within 10 ms will be cancelled:
function cancelDuplicates(fn, threshhold, scope) {
if (typeof threshhold !== 'number') threshhold = 10;
var last = 0;
return function () {
var now = +new Date;
if (now >= last + threshhold) {
last = now;
fn.apply(scope || this, arguments);
}
};
}
Usage:
button.on('click touchend', cancelDuplicates(function(event) {
// This fires right away, and calls within 10 ms after are cancelled.
}));
For me using 'onclick' in the html element itself, worked for both touch and click.
<div onclick="cardClicked(this);">Click or Touch Me</div>
I couldn't find a scroll event for meteor in the meteor docs. How do I go about doing something as someone scrolls the window down in a meteor application?
I've tried 'scroll window' : function(event) { ... } which doesn't work as expected.
I've been messing around with this as well.
I haven't found a way to do it cleanly within Template.template.events.
The obvious temporary solution right now would be using a simple jQuery scroll event.
$(window).scroll(function(){//your code}); should do the trick.
Things I was trying to use as the selector but to no avail were:
'scroll *'
'scroll body'
'scroll document'
and naturally
'scroll window'
I tried all of these selectors inside of a generic template's events, along with on UI.body's events, as that's the new blaze template that encompasses the page's body.
To reiterate: You're probably better off using jQuery for the time being.
This is a bit late but I came up with a solution; at least in the context of my current project.
I'm implementing D3 with Meteor, and I wanted a custom zoom functionality that changes the template's dimensions when the user scrolls.
Create a reactive variable 'zoom'
Template.graph.onCreated(function() {
var self = this;
self.zoom = new ReactiveVar(0);
$(window).on('scroll', function(e) {
// ... event processing stuff;
// say it produces value 'zoomAmount' ...
self.zoom.set(zoomAmount);
}
});
Create a helper that returns zoom.
Reference it in the template DOM in a hidden element to make it reactive.
Template.graph.helpers({
zoom: function() {
// This will be called when 'zoom' changes,
// so treat this as your events function
return Template.instance().zoom.get();
}
});
In Meteor there is no native template support for scroll events, so you have to do within your Template.name.onRendered callback. However, you will get a memory leak if you don't remove it from Template.name.onDestroyed. This is best accomplished with namespaced events, since something like $(window).off('scroll'); will detach all scroll events from window.
import { $ } from 'jquery';
Template.myTemplateName.onRendered(function(){
// You can do this multiple times
$(window).on('scroll.whateverNamespace', function() { ... });
$(window).on('scroll.whateverNamespace', function() { ... });
})
Template.myTemplateName.onDestroyed(function(){
$(window).off('scroll.whateverNamespace');
})
This is really late at this point, and I assume much has changed since the question was asked, but I came across this problem myself, and for anyone else that may need to know, the method that I found to work was to create a helper called 'scroll .container' where the container is a div that contains the main body of the page (where the user would scroll in my application) My function looked something like this :
Template.main_page.events({
'scroll .container': function(event) {
console.log(event.currentTarget.scrollTop);
}
});
As a partial solution, you can listen for the mousewheel event on whatever element you care about. A lot of times this is exactly what you want anyways.
As an example, the following event listener will prevent the user from scrolling with the scroll wheel at all, but they will still be able to use the navigation bar on the side of the page. (If you have not disabled it with overflowy: hidden;)
Template.body.events({
'mousewheel': function(event, template) {
console.log("scrolled");
return false;
}
});
I have implemented stickyfloat (http://plugins.jquery.com/files/stickyfloat_0.htm) on a site. It works great with one gotcha. The function triggers on $(window).scroll and not on $(window).load. I want it to trigger on either because I am linking to anchor points in the page (http://tre-stage.wdogsystems.com:8000/faq/#does-the-sale-of-my-receivables-have-a-negative-effect-on-my-credit-report) and I would like the side menu to appear when the page loads and not just when I initiate a scroll.
If you look at the page above, it's working just as I want it to. However, this is only because I've repeated the same function with a $(window).load. This seems highly inefficient to me. So, is there a way to chain the two together?
For example:
$(window).scroll || $(window).load (function () ...
jQuery's .bind()help method allows multiple events bound at once.
$(window).bind('scroll load', function() {
// code here triggers for both, scroll & load events
});
just chain in the bind, like:
$(window).bind("scroll load", ...)
however it is very bad idea to attach to scroll event
a very good explanation why and a great solution: http://ejohn.org/blog/learning-from-twitter/
Like this:
$(document).bind('ready load scroll', function() { ... });
Why don't you just trigger a window scroll event on load? You could namespace your scroll event too to isolate it and have better access to it later...
$(window)
.on('scroll.myscroll', function () {
// do something on scroll event
})
.trigger('scroll.myscroll'); // trigger scroll event on pageload
But if you were actually wanting to run it on window load (ensuring the DOM is fully loaded incl. images etc) then the other examples mentioned are fine. But use the .on() method, rather than .bind().
$(window).on('scroll.myscroll load.myload', function () {
// do something on scroll and load events
});
I know how to bind multiple events and all that stuff. What I want to do is have multiple events occur to trigger a function.
Like
$(this).click and $(this).mousemove triggers a function
Is there a way to do this? Is it possible or am I just dreaming.
With a better understanding now, one thing you could do is have one event bind and unbind the other:
Example: http://jsfiddle.net/ZMeUv/
$(myselector).mousedown( function() {
$(document).mousemove(function() {
// do something
});
$(document).mouseup(function() {
$(this).unbind(); // unbind events from document
});
});
This prevents the mousemove from constantly firing when you have no need for it.
You can use jQuery's special events to package everything nicely and optimize things in the process. A mousedown and mousemove combo also commonly goes by the name "drag", so here's an example of creating a drag event that you can bind to elements. Note, that this code is specific to jQuery 1.4.2
One of the advantages to using this is that you only bind the mousemove, mouseout, and mousedown handlers once each element, no matter how many times that element is bound to the drag event. Now this isn't the most optimal way of doing it, and you can setup just 3 handlers on the document and manage everything with it, which is equally easy to do with the special events API. It just provides a nicely packaged way of building complex interactions than would be possible with just native events or custom events, in the jQuery sense.
$("..").bind("drag", function() {
...
});
I will try and add more documentation on what's actually going on, as it looks pretty unintuitive, I must confess. Checkout another nice article on the topic.
See an example of this here. To create this custom special event, use:
jQuery.event.special.drag = {
// invoked each time we bind drag to an element
add: function(obj) {
var originalHandler = obj.handler;
obj.handler = function(event) {
var el = jQuery(this);
if(el.data('mousePressed')) {
return originalHandler.apply(this, arguments);
}
};
},
// invoked only the first time drag is bound per element
setup: function(data, namespaces) {
var el = jQuery(this);
el.data('mousePressed', false);
el.bind('mousedown', function() {
jQuery(this).data('mousePressed', true);
});
jQuery(document).bind('mouseup', function() {
el.data('mousePressed', false);
});
el.bind('mousemove', jQuery.event.special.drag.handler);
},
// invoked when all drag events are removed from element
teardown: function(namespaces) {
var el = jQuery(this);
jQuery.removeData(this, 'mousePressed');
el.unbind('mousedown');
el.unbind('mouseup');
},
// our wrapper event is bound to "mousemove" and not "bind"
// change event type, so all attached drag handlers are fired
handler: function(event) {
event.type = 'drag';
jQuery.event.handle.apply(this, arguments);
}
};
Try something like this?
var isDown = false;
$(sel).mousedown(function() {
isDown = true;
});
$(sel).mouseup(function() {
isDown = false;
});
$(sel).mousemove(function() {
if (isDown) {
// Mouse is clicked and is moving.
}
});
If I'm reading your question correctly, you're asking about requiring the combination of multiple events to trigger a single function. It's possible to achieve this sort of thing, but I think it will depend greatly on the specific events and the logic or illogic of their combination. For example, the mousemove event:
...is triggered whenever the mouse
pointer moves, even for a pixel. This
means that hundreds of events can be
generated over a very small amount of
time.
Contrast that with the mousedown event, which is -- well, one per click. How to combine? The jQuery API goes on to state:
A common pattern is to bind the
mousemove handler from within a
mousedown hander [sic], and to unbind it
from a corresponding mouseup handler.
If implementing this sequence of
events, remember that the mouseup
event might be sent to a different
HTML element than the mousemove event
was. To account for this, the mouseup
handler should typically be bound to
an element high up in the DOM tree,
such as <body>.
Perhaps another approach would be to create a primitive finite state machine to ingest as inputs the various relevant events you have in mind, update its state accordingly, and then trigger a custom event when appropriate states are achieved. This all smells a little bit like reinventing the wheel, but maybe your requirement is very specific or unusual.
References: jQuery API: mousemove()
Alright, thanks for your idea Patrick. It reminded of a way I had done something like this in Java.
var m_down = false;
$(this).mousedown(function() {
m_down = true;
});
$(this).mouseup(function() {
m_down = false;
});
$(this).mousemove(function() {
// Code to occur here
});