Не работает dbclick js
Начинаю учить javascript. Делаю крестики-нолики. Хочу сделать так чтобы при нажатии на td появлялся крестик, а при двойном нажатии -- нолик.
var one = document.querySelector("#one");
one.addEventListener('click', function() {
one.textContent = 'X';
});
one.addEventListener('dbclick', function() {
one.textContent = 'O';
});
td {
height: 150px;
width: 150px;
text-align: center;
border: 5px solid black;
font-size: 100px;
}
<table>
<tr>
<td id="one"></td>
<td id="two"></td>
<td id="three"></td>
</tr>
<tr>
<td id="four"></td>
<td id="five"></td>
<td id="six"></td>
</tr>
<tr>
<td id="seven"></td>
<td id="eight"></td>
<td id="nine"></td>
</tr>
</table>
Когда нажимаю один раз: работает, появляется крестик. Но когда делаю dbclick не работает. Как это исправить?
Ответы (1 шт):
Обработка как одиночных, так и двойных кликов (на одном и том же элементе) - достаточно просто реализуется через отложенное выполнение, по таймауту:
document.querySelector('table').addEventListener('click', evt => {
if (!evt.target.matches('td') || evt.target.textContent) return; // прерываем выполнение если клик не по ячейке, или если в ячейке уже есть отметка
const td = evt.target,
timeoutId = td.dataset.timeout; // пробуем считать ID таймаута из data-атрибута ячейки
clearTimeout(+timeoutId); // в любом случае сразу отменим запланированное выполнение (мы уже узнали, "первый ли клик" попыткой чтения ID таймаута из атрибута)
if (timeoutId) // если таймаут был выставлен (есть его ID), значит был клик - и это уже действие двойного клика!
takeCell(td, 'O');
else // иначе (ID таймаута нет), это "первый" клик - тогда запланируем отметку Х по короткому таймауту (в течение которого можно успеть кликнуть второй раз)...
td.dataset.timeout = setTimeout(() => takeCell(td, 'X'), 200); // и запоминаем ID таймаута в data-атрибуте, чтобы можно было отменить (если пользователь кликнет второй раз)
function takeCell(cell, mark) { // вспомогательная функция "взятия" ячейки игрового поля (для сокращения повторений в коде, и повышения его читаемости)
cell.textContent = mark;
delete cell.dataset.timeout; // при отметке, в любом случае удаляем ID таймаута из атрибута (т.к. сам таймаут на данном этапе уже будет снят в любом случае)
};
});
td {
width: 1em; height: 1em;
font: 100px/1 sans-serif;
text-align: center;
border: 5px solid black;
}
<table>
<tr>
<td id="one"></td>
<td id="two"></td>
<td id="three"></td>
</tr>
<tr>
<td id="four"></td>
<td id="five"></td>
<td id="six"></td>
</tr>
<tr>
<td id="seven"></td>
<td id="eight"></td>
<td id="nine"></td>
</tr>
</table>
В примере используется делегирование обработки событий - один общий обработчик (добавленный элементу таблицы) "ловит" все всплывающие события клика которые возникли и на самом элементе, и на его дочерних элементах.
Вызов clearTimeout даже когда таймаута нет, без предварительных проверок - вполне нормально, стандартное поведение функции позволяет это (никаких ошибок не возникнет).
Равно как и оператор delete не бросает ошибок когда предпринимается попытка удаления несуществующего свойства (в данном случае, этим оператором удаляется свойство объекта dataset, что повлечет удаление и data-атрибута элемента). Безусловно пользоваться этим оператором - тоже нормально.
// Для более удобного чтения комментариев в коде примера, советую развернуть его на всю страницу.