Как стать автором
Обновить

Javascript: click и dblclick на одном объекте

На днях понадобилось решить тривиальную задачу, на которую, к сожалению, ушло много времени. Задача звучит следующим образом:
Как повесить на объект два события click и dblclick, чтобы они отрабатывались правильно

Как я уже писал задача на первый взгляд простая, но как всегда есть подводные камни. Приведу пример:
<p>Example</p>

jQuery('p').click(function(){
	console.log('Click');
}).dblclick(function(){
	console.log('Dblclick');
});

При двойном клике на наш параграф в консоли вы увидите следующий результат:
Click
Click
Dblclick

Но как же так, спросите Вы, ведь браузер должен понимать разницу между двумя событиями! А вот нет. По сути, браузер понимает эти события вполне логично. Он обрабатывает первый click, потом второй. Затем все же понимает, что разница во времени между кликами очень мала и что это dblclick. На этом этапе срабатывает событие на dblclick. Также данный пример можно увидеть на официальном сайте jQuery.

Как же это обойти?


Мне в голову пришел только один вариант. Это было использование функции setTimeout. Сразу перейдем к примеру:
var timeoutId;
jQuery('p').click(function(){
	timeoutId = setTimeout('console.log("Fire")', 500);
	console.log('Click');
}).dblclick(function(){
	clearTimeout(timeoutId);
	console.log('Dblclick');
});


Из данного примера можно понять, что мы устанавливаем таймер на 500 миллисекунд на событие click. Далее при событии dblclick мы этот таймер отчищаем и не должны увидеть вывод «Fire» в консоле. А вот не тут-то было! Мы увидим такой вот результат:
Click
Click
Dblclick
Fire

Начинаем задумываться, что же не так. Вроде же все правильно. Начинаем увеличивать таймаут до пары секунд, но все равно видим сообщение “Fire”! Пытаемся разобраться, в чем же проблема:
var timeoutId;
jQuery('p').click(function(){
	timeoutId = setTimeout('console.log("Fire")', 500);
	console.log('Click Timeout = ' + timeoutId );
}).dblclick(function(){
	console.log('Dblclick Timeout = ' + timeoutId );
	clearTimeout(timeoutId);
	console.log('Dblclick Click Timeout = ' + timeoutId );
});

И получаем неожиданный для себя результат:
Click Timeout = 2
Click Timeout = 3
Dblclick Timeout = 3
Dblclick Click Timeout = 3
Fire

Для тех, кто еще не понял в чем проблема, мы выполняем функцию setTimeout два раза и соответственно Timeout каждый раз увеличивается. Когда мы пытаемся его очистить он очищает только последний. Поэтому у нас остается еще одно событие, которое срабатывает через отведенные полсекунды. Лечится это так:
var timeoutId;
jQuery('p').click(function(){
	timeoutId = setTimeout('console.log("Fire")', 500);
}).dblclick(function(){
	clearTimeout(timeoutId);
	clearTimeout(timeoutId - 1);
	console.log('Dblclick');
});

Или так:
var timeoutId;
jQuery('p').click(function(){
	if (!timeoutId)
		timeoutId = setTimeout('clearTimeout(timeoutId);console.log("Fire")', 500);
}).dblclick(function(){
	clearTimeout(timeoutId);
	console.log('Dblclick');
});

Как результат при клике мы будем видеть нужное нам сообщение «Fire», а при двойном клике «Dblclick».

Задача решена!

Если у кого-то есть идеи по-поводу более изящного решения буду благодарен за комментарии.

Спасибо.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.