I wanna achieve an effect where users can drag the screen to switch pages. I want it to print the mouse position first.
Here's a method:
dragging(ev) {
console.log(ev)
},
The dragging method is evoked when I press and move my mouse.
this.$refs.screen.addEventListener("mousedown", (ev)=>{
this.$refs.screen.addEventListener("mousemove", this.dragging)
})
But when I pass some arguments into it, the dragging method can't be evoked when I do the same thing.
this.$refs.screen.addEventListener("mousedown", (ev)=>{
this.$refs.screen.addEventListener("mousemove", this.dragging(ev))
})
I've tried several methods but I still can't solve the problem.
With each .addEventListener() you are creating a new EventListener ontop of all already added EventListeners. Your code would generate a new EventListerner when the mousebutton was pressed, which would create endless amounts of EventListeners on runtime.
Your second attempt doesn't work, as the EventListener got console.log(ev) as the callback function to execute when ever the mouse was moved.
The Approach would be to have a flag, that indicates if the mousebutton is pressed and in your mousemove EventListener you would check if the flag is true and then operate your desired logic.
// Somewhere in your data property of your component
isMouseDown = false;
// When the mousebutton is pressed, we set the flag to true
this.$refs.screen.addEventListener("mousedown", (ev)=>{
this.isMouseDown = true;
});
// When the mousebutton is released, we set the flag to false
this.$refs.screen.addEventListener("mouseup", (ev)=>{
this.isMouseDown = false;
});
// .addEventListener expects a function, that it can execute if the Event occurs.
this.$refs.screen.addEventListener("mousemove", (ev) => {
this.dragging(ev, arg1, arg2, arg3);
});
dragging(ev, arg1, arg2, arg3) {
// We only execute dragging if the isMouseDown flag is true, otherwise we quit
if(!this.isMouseDown) return;
// You operation logic
console.log(ev)
},
Related
I would like to get one event only per scroll event
I try this code but it produces "wheel" as many times the wheel event is triggered.
Any help? Thank you
window.addEventListener("wheel",
(e)=> {
console.log("wheel");
e.preventDefault();
},
{passive:false}
);
Use case (edit)
I want to allow scrolling from page to page only - with an animation while scrolling. As soon I detect the onwheel event, I would like to stop it before the animation finishes, otherwise the previous onwheel continues to fire and it is seen as new event, so going to the next of the targeted page
My conclusion :
It is not possible to cancel wheel events. In order to identify a new user wheel action while wheeling events (from a former user action) are on going, we need to calculate the speed/acceleration of such events
This is fairly simple problem, store anywhere the last direction and coditionally execute your code:
direction = '';
window.addEventListener('wheel', (e) => {
if (e.deltaY < 0) {
//scroll wheel up
if(direction !== 'up'){
console.log("up");
direction = 'up';
}
}
if (e.deltaY > 0) {
//scroll wheel down
if(direction !== 'down'){
console.log("down");
direction = 'down';
}
}
});
Anyway, the UX context should be defined.
May be that throttling or debouncing your function will give better results in some scenarios.
Throttling
Throttling enforces a maximum number of times a function can be called
over time. As in "execute this function at most once every 100
milliseconds."
Debouncing
Debouncing enforces that a function not be called again until a
certain amount of time has passed without it being called. As in
"execute this function only if 100 milliseconds have passed without it
being called.
In your case, maybe debouncing is the best option.
Temporary lock the browser scroll
$('#test').on('mousewheel DOMMouseScroll wheel', function(e) {
e.preventDefault();
e.stopPropagation();
return false;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="test">
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>6</h1>
<h1>7</h1>
<h1>8</h1>
<h1>9</h1>
<h1>10</h1>
</div>
Event.preventDefault() tells the browser not to do the default predefined action for that event, such as navigating to a page or submitting the enclosing form, etc. It does not necessarily prevent events from firing.
Also, there is a difference between the wheel event and the scroll event. The wheel event is fired when the user rotates a wheel button, and the scroll event is fired when the target's scrollTop or scrollLeft property is changed due to the scroll position being changed.
When the user rotates the wheel button, the wheel event is fired before any scroll events that could be fired. However, the wheel event might not result in any scroll event simply because the pointer is not hovering on any element or the element is not scrollable at the moment.
To aggregate quickly repeated function calls to the event handler, you can debounce the event handler function. The idea is to wait a certain amount before committing to the action. When a function is debounced, it becomes a new function, when called, sets off a timer that calls the wrapped function inside. The timer is reset and restarted when debounced function is called again. Look at the example diagram below.
© Ilya Kantor(https://github.com/javascript-tutorial/en.javascript.info, licensed under CC-BY-NC)
The function f is a debounced function with a 1000ms timeout duration and is called at time instants 0, 200ms, and 500ms with arguments a, b, and c, respectively. Because f is debounced, calls f(a) and f(b) were "not committed/ignored" because there was another call to f within a 1000ms duration. Still, call f(c) was "committed/accepted" at the time instant 1500ms because no further call followed within 1000ms.
To implement this, you can use the setTimeout and clearTimeout functions. The setTimeout function accepts an action(code or function to execute) and delay in milliseconds, then returns a timer ID in integer. The given action will be executed when the timer expires without being canceled.
const timerId = setTimeout(action, delay)
The clearTimeout function could then be used to destroy the timer with a given ID.
clearTimeout(timerId)
Following simple debounce implementation could be used:
// Default timeout is set to 1000ms
function debounce(func, timeout = 1000) {
// A slot to save timer id for current debounced function
let timer
// Return a function that conditionally calls the original function
return (...args) => {
// Immediately cancel the timer when called
clearTimeout(timer)
// Start another timer that will call the original function
// unless canceled by following call
timer = setTimeout(() => {
// Pass all arguments and `this` value
func.apply(this, args)
}, timeout)
}
}
Read more: Default parameters, Rest parameters, Function.apply(), this keyword
To use is quite simple:
eventTarget.addEventListener('wheel', debounce((e) => {
console.log('wheel', e)
}))
This will limit console.log calls to whenever a wheel event has not been fired in a second.
Live example:
function debounce(f, d = 99, t) {
return (...a) => {
clearTimeout(t)
t = setTimeout(() => {
f.apply(this, a)
}, d)
}
}
document.addEventListener('wheel', debounce((_) => {
console.log('wheel')
}))
A more modern approach uses Promise on top of this idea.
You almost had it But you need to wrap your code in a function.
I added some extra little bits so you can differentiate up and down :)
//scroll wheel manipulation
window.addEventListener('wheel', function (e) {
//TODO add delay
if (e.deltaY < 0) {
//scroll wheel up
console.log("up");
}
if (e.deltaY > 0) {
//scroll wheel down
console.log("down");
}
});
How it works?
(e) = This is just the event, the function is triggered when ever you scroll up and down, but without the function event it just doesn't know what to do! Normally people put "event" but im lazy.
deltaY = This is a function of the wheel scroll it just makes sure you scrolling along the Y axis. Its a standard inbuilt function there is no external variables you need to add.
Extras
setTimeout
You could add this. In the if statements as #Lonnie Best suggested
You could set a minimum amount of time that must pass before you consider an additional scroll event as actionable.
For example, below, 3 seconds must pass between scroll events before console.log("wheel") is fired again:
function createScrollEventHandler(milliseconds)
{
let allowed = true;
return (event)=>
{
event.preventDefault();
if (allowed)
{
console.log("wheel");
allowed = false;
setTimeout(()=>
{
allowed = true;
},milliseconds);
}
}
}
let scrollEventHandler = createScrollEventHandler(3000); // 3 seconds
window.addEventListener("wheel",scrollEventHandler);
So I'm adding a keydown listener on my page when you are focused on a certain element, but I want to remove it when you are not on this element. This works fine, but I can't find a way to pass the event object (needed for e.preventDefault()) into this function. The following is a relevant snippet of my code:
// Named function for keydown listener (I need to pass the line
// and layer objects into the function)
function keydownListener(line, layer) {
e.preventDefault();
// ... using the line and layer for various things ...
}
// adds event listener when you click on specific element
some_element.addEventListener('click', function() {(
window.addEventListener('keydown', keydownListener(line, layer));
});
// removes event listener when you click on anywhere else (not real code. just to
// demonstrate the idea)
anything_else.addEventListener('click', function() {
window.removeEventListener('keydown', keydownListener);
});
Is there no way to pass the event object into a function with other parameters?
You could try to pass the event to the keydownListener when click event is triggered
function handler(e)
keydownListener(line, layer, e)
};
// adds event listener when you click on specific element
some_element.addEventListener('click', function() {
window.addEventListener('keydown', handler)
});
// Named function for keydown listener (I need to pass the line
// and layer objects into the function)
function keydownListener(line, layer, e) {
e.preventDefault();
// ... using the line and layer for various things ...
}
The event listener function receives the event as its first argument, though some browsers have this as a global variable:
foo.addEventListener("click", function(event) {
event = event || window.event;
callOtherFunction(event, arg1, arg2);
});
I've read several answers on stackoverflow pertaining to this situation, but none of the solutions are working.
I'm trying to do different things based upon whether a user clicks an element, or holds the mouse down on that element using jQuery.
Is it possible to accomplish this?
onMouseDown will trigger when either the left or right (or middle) is pressed. Similarly, onMouseUp will trigger when any button is released. onMouseDown will trigger even when the mouse is clicked on the object then moved off of it, while onMouseUp will trigger if you click and hold the button elsewhere, then release it above the object.
onClick will only trigger when the left mouse button is pressed and released on the same object. In case you care about order, if the same object has all 3 events set, it's onMouseDown, onMouseUp, then onClick. Each even should only trigger once though.
Details:
http://api.jquery.com/click/
http://api.jquery.com/mouseup/
http://api.jquery.com/mousedown/
Here's one approach
set a variable to true
make a function that will set it to false when called
have a timer ( setTimeout() ) start counting down on mousedown()
on mouseup, clear the timeout, and check if it the variable is true or false
if it is false, call the function you want to happen on click
In any case, set the variable back to true
This will do what you want.
Here's a jsfiddle showing how it might work: http://jsfiddle.net/zRr4s/3/
Here's a solution that supports both clicks and holds:
// Timeout, started on mousedown, triggers the beginning of a hold
var holdStarter = null;
// Milliseconds to wait before recognizing a hold
var holdDelay = 500;
// Indicates the user is currently holding the mouse down
var holdActive = false;
// MouseDown
function onMouseDown(){
// Do not take any immediate action - just set the holdStarter
// to wait for the predetermined delay, and then begin a hold
holdStarter = setTimeout(function() {
holdStarter = null;
holdActive = true;
// begin hold-only operation here, if desired
}, holdDelay);
}
// MouseUp
function onMouseUp(){
// If the mouse is released immediately (i.e., a click), before the
// holdStarter runs, then cancel the holdStarter and do the click
if (holdStarter) {
clearTimeout(holdStarter);
// run click-only operation here
}
// Otherwise, if the mouse was being held, end the hold
else if (holdActive) {
holdActive = false;
// end hold-only operation here, if desired
}
}
// Optional add-on: if mouse moves out, then release hold
function onMouseOut(){
onMouseUp();
}
Here's a demo: http://jsfiddle.net/M7hT8/1/
Originally based on daveyfaherty's solution.
I know this question is from a while ago, but I'm sharing my solution for anyone who finds this via a search.
//last mouse coordinate
var mouseX = 0;
//epsilon interval
var mouseEps = 10;
function mouseDownHandler(e) {
e.preventDefault();
mouseX = e.clientX;
};
function mouseUpHandler(e) {
e.preventDefault();
if (Math.abs((mouseX - e.clientX)) < mouseEps) {
clickHandler(e);
}
};
function clickHandler(e) {
};
I want to call a js function when there is no activity from user on the web page for specified amount of time. If there is activity from user then reset timeout. I tried to search but couldn't find anything in particular. I am familiar with setTimeout() and clearTimeout() and how they work. What I am looking for is where/how to monitor for user activity. Is there any event in which I can set and clear timer?
Thank you.
Edit #1:
This webpage has one input text box & one button. It's kind of regular chat page. When I say no user activity, I mean that the user has not typed anything in text box or has not pressed any button for specified amount of time. And one more thing that it is targeted for touch based smartphone devices.
Edit #2:
Thank you everyone for suggestions. I've implemented solution based on more than one answers provided. So I will give upvote to all answers that I've found helpful instead of accepting one as answer.
// Using jQuery (but could use pure JS with cross-browser event handlers):
var idleSeconds = 30;
$(function(){
var idleTimer;
function resetTimer(){
clearTimeout(idleTimer);
idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
}
$(document.body).bind('mousemove keydown click',resetTimer); //space separated events list that we want to monitor
resetTimer(); // Start the timer when the page loads
});
function whenUserIdle(){
//...
}
Edit: Not using jQuery for whatever reason? Here's some (untested) code that should be cross-browser clean (to a point; doesn't work on IE5 Mac, for example ;):
attachEvent(window,'load',function(){
var idleSeconds = 30;
var idleTimer;
function resetTimer(){
clearTimeout(idleTimer);
idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
}
attachEvent(document.body,'mousemove',resetTimer);
attachEvent(document.body,'keydown',resetTimer);
attachEvent(document.body,'click',resetTimer);
resetTimer(); // Start the timer when the page loads
});
function whenUserIdle(){
//...
}
function attachEvent(obj,evt,fnc,useCapture){
if (obj.addEventListener){
obj.addEventListener(evt,fnc,!!useCapture);
return true;
} else if (obj.attachEvent){
return obj.attachEvent("on"+evt,fnc);
}
}
This calls for a debouncer:
function debounce(callback, timeout, _this) {
var timer;
return function(e) {
var _that = this;
if (timer)
clearTimeout(timer);
timer = setTimeout(function() {
callback.call(_this || _that, e);
}, timeout);
}
}
Used like this:
// we'll attach the function created by "debounce" to each of the target
// user input events; this function only fires once 2 seconds have passed
// with no additional input; it can be attached to any number of desired
// events
var userAction = debounce(function(e) {
console.log("silence");
}, 2000);
document.addEventListener("mousemove", userAction, false);
document.addEventListener("click", userAction, false);
document.addEventListener("scroll", userAction, false);
The first user action (mousemove, click, or scroll) kicks off a function (attached to a timer) that resets each time another user action occurs. The primary callback does not fire until the specified amount of time has passed with no actions.
Note that no global flags or timeout variables are needed. The global scope receives only your debounced callback. Beware of solutions that require maintenance of global state; they're going to be difficult to reason about in the context of a larger application.
Note also that this solution is entirely general. Beware of solutions that apply only to your extremely narrow use case.
Most JavaScript events bubble, so you could do something like the following:
Come up with a list of all the events you'd consider to be "activity from the user" (e.g., click, mousemove, keydown, etc.)
Attach one function as an event listener for all of those events to document (or maybe document.body for some of them; I can't remember if that's an issue or not).
When the listener is triggered, have it reset the timer with clearTimeout/setTimeout
So you'd end up with something like this:
var events = ['click', 'mousemove', 'keydown'],
i = events.length,
timer,
delay = 10000,
logout = function () {
// do whatever it is you want to do
// after a period of inactivity
},
reset = function () {
clearTimeout(timer);
timer = setTimeout(logout, 10000);
};
while (i) {
i -= 1;
document.addEventListener(events[i], reset, false);
}
reset();
Note that there are some issues you'd have to work out with the above code:
It's not cross-browser compatible. It only uses addEventListener, so it won't work in IE6-8
It pollutes the global namespace. It creates a lot of excess variables that might conflict with other scripts.
It's more to give you an idea of what you could do.
And now there are four other answers, but I've already typed it all up, so there :P
You want to monitor events like mousemove, keypress, keydown, and/or click at the document level.
Edit: This being a smartphone app changes what events you want to listen for. Given your textbox and button requirements, I'd listen to oninput and then add the resetTimeout() call to the click handler for your button.
var inactivityTimeout = 0;
function resetTimeout() {
clearTimeout(inactivityTimeout);
inactivityTimeout = setTimeout(inactive, 300000);
}
function inactive() {
...
}
document.getElementById("chatInput").oninput = resetTimeout;
Something like this:
function onInactive(ms, cb){
var wait = setTimeout(cb, ms);
// Bind all events you consider as activity
// Note that binding this way overrides any previous events bound the same wa
// So if you already have events bound to document, use AddEventListener and AttachEvent instead
document.onmousemove = document.mousedown = document.mouseup = document.onkeydown = document.onkeyup = document.focus = function(){
clearTimeout(wait);
wait = setTimeout(cb, ms);
};
}
IE: http://jsfiddle.net/acNfy/
Activity in the bottom right frame will delay the callback.
I'm using a nifty little 'delay' method for this that I found in this thread
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
use like
delay(function(){ doSomethingWhenNoInputFor400ms(); },400);
Also, take a look at jQuery idleTimer plugin from Paul Irish (jquery.idle-timer.js). It was based on Nicholas C. Zakas' Detecting if the user is idle with JavaScript and YUI 3 article (idle-timer.js).
It looks at similar events to the other answers, plus a few more.
events = 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove';
// activity is one of these events
There are 24 div-objects waiting/listening for a mouse-click. After click on one div-object, I want to remove the EventListener from all 24 div-objects.
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener()//Problem lies here
}
//Some other code to be run after mouseclick
},false);
}
The problem is that the removeEventListener is nested in addEventListener and I need to define type, listener, caption as attributes to the removeEventListener method. And I think it is impossible to define the listener because of nesting.
I also tried to define a function name, but it didn't worked:
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function helpme(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener('click',helpme,false);
}
//Some other code to be run after mouseclick
},false);
}
You can tell the event listener to simply fire just once:
document.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
The documentation says:
once:
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
It should work with a named function. If your second approach does not work properly, try storing the initial listener into a variable, like this:
var handler = function(event) {
for(...) {
removeEventListener('click', handler, false);
}
};
addEventListener('click', handler, false);
Ps. if you care about speed, you may wish to consider using just one event handler. You can put the handler into the parent element of the divs, and then delegate the event from there. With 24 handlers your current approach probably doesn't have a very big performance hit, but this is something you should keep in mind if it ever feels slow.
For those who needs to remove after a certain condition (or even inside a loop too), one alternative is using AbortController and AbortSignal:
const abortController = new AbortController();
let handler = function(event) {
if(...) {
abortController.abort();
}
};
addEventListener('click', handler, {signal: abortController.signal});
The same answer:
element.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
You can read more here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener