I have only one button. In css you can use button:active { do stuff } and it will become valid once and after the button is clicked, so interacting with other objects (clicking on a image) will cause
the statement to be null. How Can I translate this into java script?
Something like that:
const Ham_Button = document.querySelector('button');
while (Ham_Button.isActive)
{
do stuff
}
I tried this:
const Ham_Button = document.querySelector('button');
const ClickEvent = function() {
Hidden_Nav.style.display = "block";
}
Ham_Button.addEventListener("click", ClickEvent);
But the event is triggered only and only when I click, not after, when the element is still the last interacted object.
Maybe you can use the onblur event. With that you can detect if the user "removes" the active state of a link or button.
https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event
You can find out which element currently has focus by consulting
document.activeElement
This has good browser compatibility.
Don't put this in a while loop as in your example, though, or you'll lock up the browser's thread of execution. If you must check this periodically, consider using setTimeout.
Maybe you could try something like this:
const Ham_Button = document.querySelector('button');
let hamIsActive = false;
Ham_button.addEventListener('click', () => {
if(hamIsActive) {
// do stuff, add styles...
hamIsActive = false; // to add a toggle functionality
} else {
// do stuff, add styles...
hamIsActive = true; // to add a toggle functionality
}
})
You have the mousedown event fired once each time the button is pressed.
You also have the mouseup event fired once each time the button is released.
you can't use a while loop since it would block the main thread but you can setup a tick loop (fired roughly every 16ms, or 60 times per second)
const btn = document.getElementById('btn');
const counterDown = document.getElementById('counter_down');
let totalTimeDown = 0;
let downRequestId;
let lastTimeDown = 0;
function loopDown(time) {
if (lastTimeDown) {
const dt = time - lastTimeDown;
totalTimeDown += dt;
counterDown.innerHTML = (totalTimeDown / 1000).toFixed(2);
}
lastTimeDown = time;
downRequestId = requestAnimationFrame(loopDown);
}
function onMouseDown() {
downRequestId = requestAnimationFrame(loopDown);
}
function onMouseUp() {
lastTimeDown = 0;
cancelAnimationFrame(downRequestId);
}
btn.addEventListener('mousedown', onMouseDown);
btn.addEventListener('mouseup', onMouseUp);
// SAME LOGIC FOR FOCUS/BLUR
const counterFocus = document.getElementById('counter_focus');
let totalTimeFocused = 0;
let focusRequestId;
let lastTimeFocus = 0;
function loopFocus(time) {
if (lastTimeFocus) {
const dt = time - lastTimeFocus;
totalTimeFocused += dt;
counterFocus.innerHTML = (totalTimeFocused / 1000).toFixed(2);
}
lastTimeFocus = time;
focusRequestId = requestAnimationFrame(loopFocus);
}
function onFocus() {
focusRequestId = requestAnimationFrame(loopFocus);
}
function onBlur() {
lastTimeFocus = 0;
cancelAnimationFrame(focusRequestId);
}
btn.addEventListener('focus', onFocus);
btn.addEventListener('blur', onBlur);
button:focus {
outline: 1px solid blue;
}
button:active {
outline: 1px solid red;
}
<button id="btn">press me</button>
<div>time pressed: <span id="counter_down">0</span>s</div>
<div>time focused: <span id="counter_focus">0</span>s</div>
Note that you can switch for focus and blur events if that's what you are looking for
Related
So I encountered something weird today (at least to me). I'm trying to do a basic click event that adds and removes a class to a div with some css animations. This is an image slider I'm building. It works fine on Safari, Chrome etc on desktop. But on Iphone it seems like it only works the first time it's clicked or touched. Looks like it doesn't remove the class so it can be added again..
I did try to add this to check for user agent and adding touchstart with no luck:
var ua = navigator.userAgent,
event = ua.match(/iPad/i) || ua.match(/iPhone/) ? "touchstart" : "click";
I also added all the prefixes I could find to the css but since it is working the first time it's probably not the issue.
Hope someone have an idea of what's going on.
const clientBtn = document.querySelectorAll(".client-btn");
let clientSliderContainer = document.querySelectorAll(
".field-clientslider-container__wrapper"
);
clientSliderContainer[0].style.display = "grid";
let clientNumber = 0;
clientBtn.forEach(function (button) {
button.addEventListener('click', function () {
clientSliderContainer[clientNumber].style.display = "none";
clientSliderContainer[clientNumber].classList.remove("fadein");
if (button.classList.contains("client-slider-prev-btn")) {
clientNumber--;
if (clientNumber < 0) {
clientNumber = clientSliderContainer.length - 1;
}
clientSliderContainer[clientNumber].style.display = "grid";
clientSliderContainer[clientNumber].classList.add("fadein");
}
if (button.classList.contains("client-slider-next-btn")) {
clientNumber++;
if (clientNumber > clientSliderContainer.length - 1) {
clientNumber = 0;
}
clientSliderContainer[clientNumber].style.display = "grid";
clientSliderContainer[clientNumber].classList.add("fadein");
}
});
});
Ok so I found out it indeed was that, my class was not being removed properly. I thought I handled that by removing the class in the beginning of my listener callback. But I just made a fix where I did a setTimeout() to remove the class after a few milliseconds instead. That did the trick.
clientBtn.forEach(function (button) {
button.addEventListener('click', function (event) {
clientSliderContainer[clientNumber].style.display = "none";
if (event.currentTarget.classList.contains("client-slider-prev-btn")) {
clientNumber--;
if (clientNumber < 0) {
clientNumber = clientSliderContainer.length - 1;
}
clientSliderContainer[clientNumber].style.display = "grid";
clientSliderContainer[clientNumber].classList.add("fadein");
setTimeout(function() {
clientSliderContainer[clientNumber].classList.remove("fadein");
}, 1000);
}
if (event.currentTarget.classList.contains("client-slider-next-btn")) {
clientNumber++;
if (clientNumber > clientSliderContainer.length - 1) {
clientNumber = 0;
}
clientSliderContainer[clientNumber].style.display = "grid";
clientSliderContainer[clientNumber].classList.add("fadein");
setTimeout(function() {
clientSliderContainer[clientNumber].classList.remove("fadein");
}, 1000);
}
});
});
I have a click listener on a DOM element (no jQuery):
element.addEventListener('click', (e) => {
// some code
});
and obviously when I click, the code runs and everything is fine.
The problems is that when I double click, the code runs twice and I don't want this behavior (when I double click I want it to act like a single click and run the code once).
One possibility is to use Date to check to see if the last click that triggered the function proper was less than 1 second ago:
const element = document.querySelector('div');
let lastClick = 0;
element.addEventListener('click', (e) => {
const thisClick = Date.now();
if (thisClick - lastClick < 1000) {
console.log('quick click detected, returning early');
return;
}
lastClick = thisClick;
console.log('click');
});
<div>click me</div>
If you want the function proper to run only once the last click was more than 1 second ago (rather than the last function proper run being more than one second ago), change it so that lastClick is assigned to inside the if (thisClick - lastClick < 1000) { conditional:
const element = document.querySelector('div');
let lastClick = 0;
element.addEventListener('click', (e) => {
const thisClick = Date.now();
if (thisClick - lastClick < 1000) {
console.log('quick click detected, returning early');
lastClick = thisClick;
return;
}
lastClick = thisClick;
console.log('click');
});
<div>click me</div>
debounce the event to trigger in a certain period of time:
const element = document.querySelector('button');
let time;
element.addEventListener('click', (e) => {
if (time) {
clearTimeout(time);
}
time = setTimeout(() => console.log('runs after last click'), 500);
});
<button>Click!!!</button>
The most straightforward solution for this is to create a variable that acts as a gate that is reset after a certain time (one second in this example).
var el = document.querySelector('p');
var clickAllowed = true;
el.addEventListener('click', e => {
if (!clickAllowed) {
return;
}
clickAllowed = false;
setTimeout(() => clickAllowed = true, 1000);
// do stuff here
console.log('test');
});
<p>Test</p>
On the first click, your code will run and the "gate" will close to stop a second click. After one second, the "gate" opens to allow the code to run again.
you can set value to one of the input and see if the value is changed
function trigger(){
if(document.getElementById('isClicked').value ==0 ){
console.log('clicked for the first time');
document.getElementById('isClicked').value = 111;
setTimeout(function(){
document.getElementById('isClicked').value = 0;
}, 1000);
}
}
<button onclick='trigger()'>click me </button>
<input type='hidden' value=0 id='isClicked' />
this code working for you
var el=document.getElementById('demo');
window.clicks=0;
// handle two click
el.addEventListener('click',function(){
clicks++;
el.innerHTML='clicks: '+clicks;
setTimeout(function(){
if (clicks == 1) {
runYourCode();
clicks=0;
}
else{
clicks=0;
return;
}
},400);
})
// dblclick event
el.addEventListener('dblclick',function(){
runYourCode();
})
function runYourCode(){
document.getElementById('text').innerHTML += '<br>Run your Code...';
};
#demo{
background:red;
padding:5px 10px;
color:white;
max-width:100px;
}
<p id="demo">click me!</p>
<p id="text">
log:<br>
</p>
I am trying to make a function that would allow me to toggle eventListener of an element.
In the example below, I have three buttons: main, on and off. When I click on the on button, the main button becomes functional. After I click off button, the main button should not work anymore (but now it still does).
Now I can achieve a desired behavior by clicking on button for the second time, but I guess it's a bad coincidence and it's not supposed to work that way.
Maybe I should add that I would like to work this out without using jQuery or similar and it needs to be a function, because I am going to use it for a lot of buttons.
(I suspect something with scope causes the problem (clickHandler when calling the function to activate the button is not the same as the clickHandler when calling the function to disable the button), but I can't think of a way to test it.)
// buttons definitions, not important
var mainButton = document.querySelector("#mainButton");
var onButton = document.querySelector("#onButton");
var offButton = document.querySelector("#offButton");
// main function
var toggleButtons = function(toggleVal, button, element) {
var activateButton, clickHandler, disableButton;
// callback function for listener bellow
clickHandler = function() {
document.querySelector(element).classList.toggle("yellow");
};
activateButton = function() {
button.addEventListener("click", clickHandler);
};
disableButton = function() {
button.removeEventListener("click", clickHandler);
};
// when first argument is 1, make the button functional, otherwise disable its functionality
if (toggleVal === 1) {
activateButton();
} else {
disableButton();
}
};
// when onButton is clicked, call main function with arguments
// this works
onButton.addEventListener("click", function() {
toggleButtons(1, mainButton, "body");
});
// this fails to disable the button
offButton.addEventListener("click", function() {
toggleButtons(0, mainButton);
});
.yellow {
background-color: yellow;
}
<button type="button" id="mainButton">mainButton
</button>
<button type="button" id="onButton">onButton
</button>
<button type="button" id="offButton">offButton
</button>
<p>mainButton: toggles background color on click
</p>
<p>onButton: turns on mainButtons's functionality</p>
<p>offButton: supposed to turn off mainButton's functionality</p>
var mainButton = document.querySelector("#mainButton");
var onButton = document.querySelector("#onButton");
var offButon = document.querySelector("#offButton");
var element; // declare the element here and change it from toggleButtons when needed.
function clickHandler() {
document.querySelector(element).classList.toggle('yellow');
}
function activateButton(button) { // You missed this part
button.addEventListener('click', clickHandler);
}
function disableButton(button) { // You missed this part
button.removeEventListener('click', clickHandler);
}
function toggleButtons(value, button) {
if (value === 1) {
activateButton(button); // You missed this part
} else {
disableButton(button); // You missed this part
}
};
onButton.addEventListener('click', function() {
element = 'body'; // you can change it to some other element
toggleButtons(1, mainButton);
});
offButton.addEventListener('click', function() {
element = 'body'; // you can change it to some other element
toggleButtons(0, mainButton);
});
Below code helps to toggle between two functions from an eventListener:
var playmusic=false;
function playSound() {
const audio = document.querySelector(`audio[data-key="${event.keyCode}"]`)
audio.currentTime = 0
audio.play()
playmusic=true;
}
function stopSound() {
const audio = document.querySelector(`audio[data-key="${event.keyCode}"]`)
audio.pause()
playmusic=false;
}
window.addEventListener('keydown',
function(){playmusic?stopSound():playSound()} )
I want to achieve the double click event on a specific div like this:
<div id="divID" ondblclick = 'alert("double click!!");' >
it worked on the google chrome browser but when I open it with phone it didn't work, by the way the single click worked.
ps: i added this two things
<meta name="viewport" content="width=device-width, initial scale=1,user-scalable=no">
and this
body {
-ms-touch-action: manipulation;
touch-action: manipulation;}
but it didnt work!
I got the same issue. On touch devices, if you want to detect a double-tap gesture and you use the ondblclick event in most cases it will not work and also the problem is it will also fire an onclick. One of the solution is to implement a double tap detection pattern using the following code sample:
var doubletapDeltaTime_ = 700;
var doubletap1Function_ = null;
var doubletap2Function_ = null;
var doubletapTimer = null;
function tap(singleTapFunc, doubleTapFunc) {
if (doubletapTimer==null) {
// First tap, we wait X ms to the second tap
doubletapTimer_ = setTimeout(doubletapTimeout_, doubletapDeltaTime_);
doubletap1Function_ = singleTapFunc;
doubletap2Function_ = doubleTapFunc;
} else {
// Second tap
clearTimeout(doubletapTimer);
doubletapTimer_ = null;
doubletap2Function_();
}
}
function doubletapTimeout() {
// Wait for second tap timeout
doubletap1Function_();
doubleTapTimer_ = null;
}
And you can call it like
<div id="divID" onclick="tap(tapOnce, tapTwice)" >
tapOnce and tapTwice are your functions which will be called in respective cases. This solution will work on browsers too.
Reference
Here is the external function 'doubletap' which can be helpful:
/*
* jQuery Double Tap
* Developer: Sergey Margaritov (sergey#margaritov.net)
* Date: 22.10.2013
* Based on jquery documentation http://learn.jquery.com/events/event-extensions/
*/
(function($){
$.event.special.doubletap = {
bindType: 'touchend',
delegateType: 'touchend',
handle: function(event) {
var handleObj = event.handleObj,
targetData = jQuery.data(event.target),
now = new Date().getTime(),
delta = targetData.lastTouch ? now - targetData.lastTouch : 0,
delay = delay == null ? 300 : delay;
if (delta < delay && delta > 30) {
targetData.lastTouch = null;
event.type = handleObj.origType;
['clientX', 'clientY', 'pageX', 'pageY'].forEach(function(property) {
event[property] = event.originalEvent.changedTouches[0][property];
})
// let jQuery handle the triggering of "doubletap" event handlers
handleObj.handler.apply(this, arguments);
} else {
targetData.lastTouch = now;
}
}
};
})(jQuery);
Load jQuery Mobile into your project and try using taphold or some of the other mobile specific touch events that are available to you through that API.
Here's the jQuery Mobile documentation with all the events you can use: http://api.jquerymobile.com/category/events/
Here is the snippet for TS React users. Pass in the click event, so that double click is only invoked if the same element is clicked twice
import React from "react";
type CallBack = () => any;
type TapParams = { onSingleTap?: CallBack; onDoubleTap?: CallBack };
var DELTA_TIME_THRESHOLD_MS = 700;
var timer: NodeJS.Timeout | null = null;
var target: EventTarget;
export function tap(
e: React.MouseEvent,
{ onSingleTap, onDoubleTap }: TapParams
) {
if (timer == null) {
// First tap
onSingleTap?.();
timer = setTimeout(() => {
timer = null;
}, DELTA_TIME_THRESHOLD_MS);
} else {
// Second tap
if (e.target === target) {
onDoubleTap?.();
}
clearTimeout(timer);
timer = null;
}
target = e.target;
}
Usage
<div
onClick={(e) => tap(e, { onSingleTap, onDoubleTap })}
>Tap or doubletap</div>
Using only JavaScript
You can use "touchstart" event for a single touch,
but with calculating the time when he should click again
I used 400 (0.4s) as it's the longer duration between two touches
It's only an estimate, but it's still a reasonable time
let expired
let doubleClick = function () {
console.log('double click')
}
let doubleTouch = function (e) {
if (e.touches.length === 1) {
if (!expired) {
expired = e.timeStamp + 400
} else if (e.timeStamp <= expired) {
// remove the default of this event ( Zoom )
e.preventDefault()
doubleClick()
// then reset the variable for other "double Touches" event
expired = null
} else {
// if the second touch was expired, make it as it's the first
expired = e.timeStamp + 400
}
}
}
let element = document.getElementById('btn')
element.addEventListener('touchstart', doubleTouch)
element.addEventListener('dblclick', doubleClick)
In case of this error :
Unable to preventDefault inside passive event listener due to target being treated as passive.
event.preventDefault( ) not working if element = "document" or "document.body"
So the solution of that, you should have a full page div container :
let element = document.getElementById('container')
element.style.minWidth = '100vw'
element.style.minHeight = '100vh'
document.body.style.margin = '0px'
element.addEventListener('touchstart', elementTouch)
element.addEventListener('dblclick', doubleClick)
I want to disable the default context menu that appears after a certain text is selected in iOS Safari (web browser). Is that possible?
The only way i found was by removing the selection and select again with javascript.
Have a look at my code:
/* prevent ios edit-menu */
if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
!function(){
var target = document.body; // the element where the edit menue should be disabled
var preventSelRecursion;
document.addEventListener('selectionchange', function(e){
var S = getSelection();
if (!S.rangeCount) return;
if (S.isCollapsed) return;
var r = S.getRangeAt(0);
if (!target.contains(r.commonAncestorContainer)) return;
if (preventSelRecursion) return;
iosSelMenuPrevent();
}, false);
var iosSelMenuPrevent = debounce(function(){
var S = getSelection();
var r = S.getRangeAt(0);
preventSelRecursion = true;
S = getSelection();
S.removeAllRanges();
setTimeout(function(){ // make remove-add-selection removes the menu
S.addRange(r);
setTimeout(function(){
preventSelRecursion = false;
});
},4);
},800); // if no selectionchange during 800ms : remove the menu
/* helper-function */
function debounce(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
}();
}
It is possible, see this example. Basically, the important part is to set the right css atributes:
body { -webkit-touch-callout: none !important; }
a { -webkit-user-select: none !important; }
Also, here is a question which solves a similar issue
According to onclick blocks copy+paste on Mobile Safari?, if the text is in an element that has an onclick event, the context menu won't be displayed.
Inspired by Hans Gustavson's answer, I propose a simpler solution in TypeScript:
function disableIosSafariCallout(this: Window, event: any) {
const s = this.getSelection();
if ((s?.rangeCount || 0) > 0) {
const r = s?.getRangeAt(0);
s?.removeAllRanges();
setTimeout(() => {
s?.addRange(r!);
}, 50);
}
}
document.ontouchend = disableIosSafariCallout.bind(window);
This solution is actually a workaround. When you select a text, you might still see the text selection callout shows and then disappear immediately. I am not sure whether Hans Gustavson's answer has the same defect...