js differ click and double click - javascript

I am trying to differentiate click and dblclick events by javascript. I tried stopPropagation in dblclick however did not work. hence I coded my own function using setTimeout tries to avoid calling the other when calling one.
/* dblclick differ click
takes two functions as param and one element
to bind the event listener on
*/
function cdblclick(click, dblclick, el){
el.cooled = true;
el.addEventListener('click', function(){
if(el.cooled){
setTimeout(function(){
if(!el.cdblc){el.cdblc = 0}
el.cdblc++;
if(el.cdblc === 1){
setTimeout(function(){
if(!el.doubled){
console.log('click')
// click();
el.cdblc = 0;
el.cooled = true;
el.doubled = false;
}
}, 300);
}else if(el.cdblc === 2){
console.log('double')
// dblclick();
el.doubled = true;
el.cdblc = 0;
el.cooled = true;
}else{
}
}, 250);
}
});
}
however, it does not work properly. I will be so glad if you ca give me a hand.
Thanks for the suggestions of divide to two elements, however, I must implement both events on the same element
solution
thanks to all the helps.
/* dblclick differ click
takes two functions as param and one element
to bind the event listener on
*/
function cdblclick(click, dblClick, el) {
let clickedTimes = 0;
const incrementClick = () => {
clickedTimes++;
};
const reset = () => {
clickedTimes = 0;
};
el.addEventListener('click', e => {
incrementClick();
setTimeout(() => {
if (clickedTimes === 1) {
click(e);
} else if (clickedTimes >= 2) {
dblClick(e);
}
reset();
}, 300);
});
}

Here's a solution where each 'click counter' is specific to each element, so onclick functions outside the element will not be effected...
const singleOrDouble = ({target}) => {
if (isNaN(target.cn)) {
target.cn = 1
} else {
target.cn++
}
setTimeout(() => {
if (target.cn == 1) {
console.log('once')
target.cn = 0
}
if (target.cn > 1) {
console.log('twice')
target.cn = 0
}
}, 500)
}
<p onclick="singleOrDouble(event)">Click me</p>

If you dont want to execute the single click at all in case of double, you can use a timeout like you did. You just have to reset the counter and the timeout properly. You do not reset el.cdblc on your last empty else.
Be aware, that this delays normal clicks.
/* dblclick differ click
takes two functions as param and one element
to bind the event listener on
*/
function cdblclick(click, dblclick, el){
//REM: Storing the values in an object instead of own element properties
let tObject = {
Clicks: 0,
onClick: click,
onDblClick: dblclick
};
//el.cooled = true;
el.addEventListener('click', function(object){
object.Clicks += 1;
//REM: Starting the click..
if(object.Clicks === 1){
object.Timeout = window.setTimeout(function(){
//REM: Execute click if not stopped
if(typeof object.onClick == 'function'){
object.onClick()
};
//REM: Resetting the click count
object.Clicks = 0
/*if(!el.cdblc){el.cdblc = 0}
el.cdblc++;
if(el.cdblc === 1){
setTimeout(function(){
if(!el.doubled){
console.log('click')
// click();
el.cdblc = 0;
el.cooled = true;
el.doubled = false;
}
*/
}.bind(this, object), 300)
}
//REM: Unless we have triple clicks, there is only double left
else{
//REM: Cancel the single click
window.clearTimeout(object.Timeout);
//REM: Execute double click
if(typeof object.onDblClick === 'function'){
object.onDblClick()
};
//REM: Resetting the click count
object.Clicks = 0
/*
console.log('double')
// dblclick();
el.doubled = true;
el.cdblc = 0;
el.cooled = true;
*/
}
}.bind(el, tObject))
};
document.querySelector('b').onclick = cdblclick(
function(){console.log('onclick')},
function(){console.log('ondblclick')},
document.body
);
<b>Click me</b>

Like this will work.
function cDblClick(click, dblClick, el) {
let clickedTimes = 0;
const incrementClick = () => {
clickedTimes++;
};
const reset = () => {
clickedTimes = 0;
};
el.addEventListener('click', () => {
incrementClick();
setTimeout(() => {
if (clickedTimes === 1) {
click();
} else if (clickedTimes === 2) {
dblClick();
}
reset();
}, 300);
});
}

you can try ondblclick js listner like this :
document.getElementById('click').onclick = function() { console.log("click"); }
document.getElementById('dblclick').ondblclick = function() { console.log("double click"); }
<div>
<button id="click"> click </button>
<button id="dblclick"> double click </button>
</div>

You can try this way to differ click and dbclick:
document.getElementById('click').onclick = function() {
//do something if click
}
document.getElementById('click').ondblclick = function() {
//do something if dbclick
}
<div>
<button id="click"> click or dbclick? </button>
</div>

Related

JavaScript: Change text color depending on video.currentTime

I have created navigation buttons for my video, some sort of timestamps that allow a user to jump to a certain moment in the video on click. The buttons change their color whenever they are clicked and the value of video.currentTime is in a particular range. When one button is red, the rest must be white ofcourse.
Take a look at my codepen: https://codepen.io/anon/pen/VRZPEP
My solution does work. However, I feel like it's too complicated and the feature could be done much simpler and with less code.
The important bit of javascript is under the //VIDEO NAV BUTTON FUNCTION header.
I'm just wondering if there's any way I could get rid of the changeToWhite() function? For example, by using some method making the button red only when the video.currentTime finds itself within a particular range?
var DOMstrings = {
nav_btn: document.querySelector('.navbar-button'),
nav_btn_mobile: document.querySelector('.navbar-toggler'),
nav_list: document.querySelector('#wrapper-for-list'),
//video
video: document.querySelector('#myVideo'),
//video-nav-buttons
nav_btn_1: document.querySelector('.video-nav h1:nth-child(1)'),
nav_btn_2: document.querySelector('.video-nav h1:nth-child(2)'),
nav_btn_3: document.querySelector('.video-nav h1:nth-child(3)'),
nav_btn_4: document.querySelector('.video-nav h1:nth-child(4)'),
nav_btn_5: document.querySelector('.video-nav h1:nth-child(5)')
}
//VIDEO NAV BUTTON FUNCTION
var navigateVideo = function() {
var video_nav = [DOMstrings.nav_btn_1, DOMstrings.nav_btn_2, DOMstrings.nav_btn_3, DOMstrings.nav_btn_4, DOMstrings.nav_btn_5]
var setTime = function(time) {
DOMstrings.video.currentTime = time;
};
var changeToWhite = function() {
video_nav.forEach(function(cur) {
cur.style.color = "white";
});
};
DOMstrings.video.addEventListener('timeupdate', function() {
var cur = DOMstrings.video.currentTime;
if (cur >= 0 && cur < 5) {
changeToWhite();
DOMstrings.nav_btn_1.style.color = "red";
} else if (cur > 5 && cur < 10) {
changeToWhite();
DOMstrings.nav_btn_2.style.color = "red";
} else if (cur > 10 && cur < 15) {
changeToWhite();
DOMstrings.nav_btn_3.style.color = "red";
} else if (cur > 15 && cur < 20) {
changeToWhite();
DOMstrings.nav_btn_4.style.color = "red";
} else if (cur > 25) {
changeToWhite();
DOMstrings.nav_btn_5.style.color = "red";
}
});
DOMstrings.nav_btn_1.addEventListener('click', function(e) {
setTime(0);
changeToWhite();
e.target.style.color = "red";
});
DOMstrings.nav_btn_2.addEventListener('click', function(e) {
setTime(15);
changeToWhite();
e.target.style.color = "red";
});
DOMstrings.nav_btn_3.addEventListener('click', function(e) {
setTime(30);
changeToWhite();
e.target.style.color = "red";
});
DOMstrings.nav_btn_4.addEventListener('click', function(e) {
setTime(45);
changeToWhite();
e.target.style.color = "red";
});
DOMstrings.nav_btn_5.addEventListener('click', function(e) {
setTime(60);
changeToWhite();
e.target.style.color = "red";
});
};
You could simplify this a little by renaming changeToWhite() to changeToRed(button):
const changeToRed = (button) => {
video_nav.forEach(cur => {
button.style.color = (cur === button) ? 'red' : 'white';
})
}
Then pass in each button when you call it:
changeToRed(DOMstrings.nav_btn_1);
// Previously:
// changeToWhite();
// DOMstrings.nav_btn_1.style.color = "red";
A few other things: You could make a single function that takes in a time in seconds instead of repeating the same function 5 times:
function handleClick(seconds) {
return function(e) {
setTime(seconds);
changeToRed(e.target);
}
}
DOMstrings.nav_btn_1.addEventListener('click', handleClick(0));
DOMstrings.nav_btn_2.addEventListener('click', handleClick(15));
DOMstrings.nav_btn_3.addEventListener('click', handleClick(30));
...etc
You could also get rid of the if/else block by doing this instead:
const cur = DOMstrings.video.currentTime;
// Divide the current time by 5 and round down, so 0-4 seconds
// would use index 0 (nav_btn_1), 5-9 seconds would use index
// 1 (nav_btn_2), etc
const buttonIndex = Math.floor(cur / 5);
changeToRed(video_nav[buttonIndex]);
You can consolidate logic in the timeUpdate listener, and you could also eliminate much repetition in the click listeners by replacing them all with a single click listener on the buttons' parent element. (This works because of event bubbling.) Altogether, it might look something like:
// Selectors
const video = document.querySelector("#myVideo");
const videoNav = document.querySelector(".video-nav");
const buttons = document.querySelectorAll(".video-nav button");
//Bindings
video.addEventListener("timeupdate", updateTime);
videoNav.addEventListener("click", conditionallySetTime);
//Listeners
function updateTime(event){
const time = event.target.time;
buttons.forEach(function(button, index){
button.style.color = index == Math.floor(time/15) ? "red" : "white"
});
}
function conditionallySetTime(event){
if(event.target.tagName == button){ // Now we care about this click
buttons.forEach(function(button, index){
if(event.target == button){
button.style.color = "red";
setTime(index * 15); // If 1st button is 0, 2nd is 15, etc.
}
else {
button.style.color = "white";
}
}
}
}

Resetting variables is not resetting game?

I am creating a Simon game and it works on round one, but once your each round two it immediately says incorrect.
jsfiddle
I reset all variables between the rounds, but it still seems to save them as it instantly says "Game Over!"
JS:
function makeGuess() {
...
var checkForComplete = setInterval(function () {
if (yourAnswer.length >= round) {
clearInterval(checkForComplete);
yourString = yourAnswer.toString();
correctString = correctAnswer.toString();
if (yourString === correctString) {
alert('Correct');
round++;
yourAnswer = [];
correctAnswer = [];
yourString = "";
correctString = "";
playSimon();
}
else {
alert("Game Over!");
}
}
}, 500)
Here is the playSimon() function:
function playSimon() {
setTimeout(function () {
color = Math.floor(Math.random() * 4);
if (color == 0) {
correctAnswer.push(0);
$('#redButton').addClass('lightOn');
setTimeout(function () {
$('#redButton').removeClass('lightOn');
}, 500);
}
else if (color == 1) {
correctAnswer.push(1);
$('#blueButton').addClass('lightOn');
setTimeout(function () {
$('#blueButton').removeClass('lightOn');
}, 500);
}
else if (color == 2) {
correctAnswer.push(2);
$('#greenButton').addClass('lightOn');
setTimeout(function () {
$('#greenButton').removeClass('lightOn');
}, 500);
}
else if (color == 3) {
correctAnswer.push(3);
$('#yellowButton').addClass('lightOn');
setTimeout(function () {
$('#yellowButton').removeClass('lightOn');
}, 500);
}
i++;
if (i <= round) {
playSimon();
}
else {
makeGuess();
}
}, 700);
}
Why is it alerting Game Over instantly on round 2?
Because you are binding the click events multiple times:
function makeGuess() {
$('#redButton').click(function() {
yourAnswer.push(0);
});
$('#blueButton').click(function() {
yourAnswer.push(1);
});
$('#greenButton').click(function() {
yourAnswer.push(2);
});
$('#yellowButton').click(function() {
yourAnswer.push(3);
});
...
}
Each time makeGuess() is called, the buttons are bound yet another click event listener. First time it's done (first level), there's only 1 event listener so it's working correctly, but from the second level onward each buttons are bound with 2 identical click listener, and when the player click a button, the function is called twice => yourAnswer is pushed twice immediately, which result in an incorrect answer => game over.
A quick fix is to unbind all click events on the 4 buttons before binding them all again, optimally immediately before the next playSimon() is called.
if (yourString === correctString) {
alert('Correct');
round++;
yourAnswer = [];
correctAnswer = [];
yourString = "";
correctString = "";
$('#redButton').unbind( "click" );
$('#blueButton').unbind( "click" );
$('#greenButton').unbind( "click" );
$('#yellowButton').unbind( "click" );
playSimon();
} else {
alert("Game Over!");
}
JsFiddle: https://jsfiddle.net/dfk2am7e/1/
AVAVT is right, however instead of unbinding, rather just bind the event once off.
I have updated your JSFiddle and optimized your code a little bit.
https://jsfiddle.net/dfk2am7e/3/
// Run this when the page has loaded to ensure HTML is there.
$(function(){
$('#redButton').click(function() {
yourAnswer.push(0);
});
$('#blueButton').click(function() {
yourAnswer.push(1);
});
$('#greenButton').click(function() {
yourAnswer.push(2);
});
$('#yellowButton').click(function() {
yourAnswer.push(3);
});
});

JQuery - bind() again when count is reset

In my game I want to make sure the start button is only clicked once each time it appears to stop it loading the function more than once.
I have written some code that counts the clicks and unbinds if the count is greater than zero.
var oneClick = 0;
$(".start-btn-wrapper").bind("click", function () {
oneClick++;
if (oneClick > 0) {
$(this).unbind("click");
}
newGame();
});
This works fine but I need a way to bind it again ready for the next time it appears (When the restart-btn is clicked) so I have written this to reset the count.
$(".restart-btn").click(function () {
resetGame();
oneClick = 0;
});
For some reason this doesn't do the job and I am looking to SO for a soloution
You're not binding again but just changing oneClick, which isn't even tested as the event handler to $(".start-btn-wrapper") isn't binded anymore.
But instead of unbinding/rebinding, which is costly, why not just test oneClick :
$(".start-btn-wrapper").bind("click", function () {
if (oneClick++ > 0) return;
newGame();
}
Note that if you just need two states, a boolean would be clearer :
var clickable = true;
$(".start-btn-wrapper").bind("click", function () {
if (!clickable) return;
newGame();
clickable = false;
}
$(".restart-btn").click(function () {
resetGame();
clickable = true;
});
jQuery has a built in method for exactly this use. It is .one()
So all you have to do is
$(".start-btn-wrapper").one('click',newGame);
and for the restart
$(".restart-btn").click(function () {
resetGame();
$(".start-btn-wrapper").one('click',newGame);
});
simple
var oneClick = 0;
$(".start-btn-wrapper").bind("click", function () {
if (oneClick > 0) {
return false;
}
oneClick++;
newGame();
});
$(".restart-btn").click(function () {
resetGame();
oneClick = 0;
});
var oneClick = 0;
function clickHandler() {
oneClick++;
if (oneClick > 0) {
$(".start-btn-wrapper").unbind("click");
}
newGame();
}
$(".start-btn-wrapper").bind("click", clickHandler);
$(".restart-btn").click(function () {
resetGame();
oneClick = 0;
$(".start-btn-wrapper").bind("click", clickHandler);
});

Clear a non-global timeout launched in jQuery plug-in

I try to set a timeout on an element, fired with a jQuery plugin. This timeout is set again in the function depending on conditions. But, I want to clear this element's timeout before set another (if I relaunch the plug-in), or clear this manually.
<div id="aaa" style="top: 0; width: 100px; height: 100px; background-color: #ff0000;"></div>
Here's my code (now on http://jsfiddle.net/Ppvf9/)
$(function() {
$('#aaa').myPlugin(0);
});
(function($) {
$.fn.myPlugin = function(loops) {
loops = loops === undefined ? 0 : loops;
this.each(function() {
var el = $(this),
loop = loops,
i = 0;
if (loops === false) {
clearTimeout(el.timer);
return;
}
var animate = function() {
var hPos = 0;
hPos = (i * 10) + 'px';
el.css('margin-top', hPos);
if (i < 25) {
i++;
} else {
if (loops === 0) {
i = 0;
} else {
loop--;
if (loop === 0) {
return;
} else {
i = 0;
}
}
}
el.timer = window.setTimeout(function () {
animate();
}, 1000/25);
};
clearTimeout(el.timer);
//$('<img/>').load(function() {
// there's more here but it's not very important
animate();
//});
});
return this;
};
})(jQuery);
If I make $('#element').myPlugin();, it's launched. If I make it a second time, there's two timeout on it (see it by doing $('#aaa').myPlugin(0);
in console). And I want to be able to clear this with $('#element').myPlugin(false);.
What am I doing wrong?
EDIT :
SOLVED by setting two var to access this and $(this) here : http://jsfiddle.net/Ppvf9/2/
try saving the timeout handle as a property of the element. Or maintain a static lookup table that maps elements to their timeout handles.
Something like this:
el.timer = window.setTimeout(...);
I assume you need one timer per element. Not a single timer for all elements.

How do I listen for triple clicks in JavaScript?

If this is for a double-click:
window.addEventListener("dblclick", function(event) { }, false);
How can I capture a triple-click? This is for a pinned tab in Google Chrome.
You need to write your own triple-click implementation because no native event exists to capture 3 clicks in a row. Fortunately, modern browsers have event.detail, which the MDN documentation describes as:
A count of consecutive clicks that happened in a short amount of time, incremented by one.
This means you can simply check the value of this property and see if it is 3:
window.addEventListener('click', function (evt) {
if (evt.detail === 3) {
alert('triple click!');
}
});
Working demo: http://jsfiddle.net/L6d0p4jo/
If you need support for IE 8, the best approach is to capture a double-click, followed by a triple-click — something like this, for example:
var timer, // timer required to reset
timeout = 200; // timer reset in ms
window.addEventListener("dblclick", function (evt) {
timer = setTimeout(function () {
timer = null;
}, timeout);
});
window.addEventListener("click", function (evt) {
if (timer) {
clearTimeout(timer);
timer = null;
executeTripleClickFunction();
}
});
Working demo: http://jsfiddle.net/YDFLV/
The reason for this is that old IE browsers will not fire two consecutive click events for a double click. Don't forget to use attachEvent in place of addEventListener for IE 8.
Since DOM Level 2 you could use mouse click handler and check the detail parameter of event which should be interpreted as:
The detail attribute inherited from UIEvent indicates the number of times a mouse button has been pressed and released over the same screen location during a user action. The attribute value is 1 when the user begins this action and increments by 1 for each full sequence of pressing and releasing. If the user moves the mouse between the mousedown and mouseup the value will be set to 0, indicating that no click is occurring.
So the value of detail === 3 will give you the triple-click event.
More information in specification at http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-MouseEvent.
Thanks to #Nayuki https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail - a DOM3 extension which is WIP https://w3c.github.io/uievents/
Here is the real Triple click event, which triggers only when all of three clicks fired with equal interval.
// Default settings
var minClickInterval = 100,
maxClickInterval = 500,
minPercentThird = 85.0,
maxPercentThird = 130.0;
// Runtime
var hasOne = false,
hasTwo = false,
time = [0, 0, 0],
diff = [0, 0];
$('#btn').on('click', function() {
var now = Date.now();
// Clear runtime after timeout fot the 2nd click
if (time[1] && now - time[1] >= maxClickInterval) {
clearRuntime();
}
// Clear runtime after timeout fot the 3rd click
if (time[0] && time[1] && now - time[0] >= maxClickInterval) {
clearRuntime();
}
// Catch the third click
if (hasTwo) {
time[2] = Date.now();
diff[1] = time[2] - time[1];
var deltaPercent = 100.0 * (diff[1] / diff[0]);
if (deltaPercent >= minPercentThird && deltaPercent <= maxPercentThird) {
alert("Triple Click!");
}
clearRuntime();
}
// Catch the first click
else if (!hasOne) {
hasOne = true;
time[0] = Date.now();
}
// Catch the second click
else if (hasOne) {
time[1] = Date.now();
diff[0] = time[1] - time[0];
(diff[0] >= minClickInterval && diff[0] <= maxClickInterval) ?
hasTwo = true : clearRuntime();
}
});
var clearRuntime = function() {
hasOne = false;
hasTwo = false;
time[0] = 0;
time[1] = 0;
time[2] = 0;
diff[0] = 0;
diff[1] = 0;
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click button three times with equal interval
<button id="btn">Click me</button>
Also, I wrote jquery plugin TrplClick, which enables 'trplclick' event
it's very simple if you do it right, and you can even catch single, double, triple, ... clicks as you like. plain javascript, customizable click delay (timeout):
var clicks = 0;
var timer, timeout = 350;
var doubleClick = function(e) {
console.log('doubleClick');
}
var tripleClick = function(e) {
console.log('tripleClick');
}
// click timer
yourcontainer.addEventListener('click', function(e) {
clearTimeout(timer);
clicks++;
var evt = e;
timer = setTimeout(function() {
if(clicks==2) doubleClick(evt);
if(clicks==3) tripleClick(evt);
clicks = 0;
}, timeout);
});
pseudo-code:
var clicks = 0
onclick:
clicks++;
setTimer(resetClicksToZero);
if clicks == 3: tripleclickdetected(); clicks = 0;
I am working on a javascript code editor and I had to listen for triple click and here is the solution that will work for most browsers:
// Function to get mouse position
var getMousePosition = function (mouseEvent) {
var currentObject = container;
var currentLeft = 0;
var currentTop = 0;
do {
currentLeft += currentObject.offsetLeft;
currentTop += currentObject.offsetTop;
currentObject = currentObject.offsetParent;
} while (currentObject != document.body);
return {
x: mouseEvent.pageX - currentLeft,
y: mouseEvent.pageY - currentTop
}
}
// We will need a counter, the old position and a timer
var clickCounter = 0;
var clickPosition = {
x: null,
y: null
};
var clickTimer;
// The listener (container may be any HTML element)
container.addEventListener('click', function (event) {
// Get the current mouse position
var mousePosition = getMousePosition(event);
// Function to reset the data
var resetClick = function () {
clickCounter = 0;
var clickPosition = {
x: null,
y: null
};
}
// Function to wait for the next click
var conserveClick = function () {
clickPosition = mousePosition;
clearTimeout(clickTimer);
clickTimer = setTimeout(resetClick, 250);
}
// If position has not changed
if (clickCounter && clickPosition.x == mousePosition.x && clickPosition.y == mousePosition.y) {
clickCounter++;
if (clickCounter == 2) {
// Do something on double click
} else {
// Do something on triple click
resetClick();
}
conserveClick();
} else {
// Do something on single click
conserveClick();
}
});
Tested on Firefox 12, Google Chrome 19, Opera 11.64, Internet Explorer 9
This approach checks if the user has not changed cursor's position, you still can do something when you have single click or double click. Hope this solution will help everybody who will need to implement a triple click event listener :)
Configurable n-clicks event detector factory
const nClicks = (minClickStreak, maxClickInterval = 500, resetImmediately = true) => {
let timerId = 0
let clickCount = 0
let lastTarget = null
const reset = () => {
timerId = 0
clickCount = 0
lastTarget = null
}
return (originalEventHandler) => (e) => {
if (lastTarget == null || lastTarget == e.target) { // 2. unless we clicked same target
clickCount++ // 3. then increment click count
clearTimeout(timerId)
}
lastTarget = e.target
timerId = setTimeout(reset, maxClickInterval) // 1. reset state within set time
if (clickCount >= minClickStreak) {
originalEventHandler(e)
if (resetImmediately) {
clickCount = 0
}
}
}
}
Usage
table.addEventListener('click', nClicks(2)(e => { // double click
selectCell(e.target)
}))
table.addEventListener('click', nClicks(3)(e => { // triple click
selectRow(e.target)
}))

Categories