Я хочу обнаружить , где произошел MouseEvent
, в координатах относительно щелкнутого элемента. Зачем? Потому что я хочу добавить абсолютно позиционированный дочерний элемент в выбранном месте.
Я знаю, как его обнаружить, когда нет преобразований CSS3 (см. описание ниже). Однако, когда я добавляю CSS3 Transform, тогда мой алгоритм прерывается, и я не знаю, как его исправить.
Я не использую какую-либо библиотеку JavaScript, и я хочу понять, как все работает в простом JavaScript. Поэтому, пожалуйста, не отвечайте "просто используйте jQuery".
Кстати, я хочу решение, которое работает для всех MouseEvents, а не только "click". Не то чтобы это важно, потому что я считаю, что все события мыши имеют одни и те же свойства, поэтому одно и то же решение должно работать для всех.
Фоновая информация
Согласно спецификация DOM уровня 2, MouseEvent
имеет несколько свойств, связанных с получением координат события:
-
screenX
иscreenY
вернуть координаты экрана (начало координат находится в верхнем левом углу пользовательского монитора) -
clientX
иclientY
вернуть координаты относительно окна просмотра документа.
Таким образом, чтобы найти положение MouseEvent
относительно содержимого элемента clicked, я должен выполнить эту математику:
ev.clientX - this.getBoundingClientRect().left - this.clientLeft + this.scrollLeft
-
ev.clientX
- это координата относительно окна просмотра документа. -
this.getBoundingClientRect().left
- это позиция элемента относительно окна просмотра документа. -
this.clientLeft
- количество границы (и полосы прокрутки) между границей элемента и внутренними координатами -
this.scrollLeft
- количество прокрутки внутри элемента
getBoundingClientRect()
, clientLeft
и scrollLeft
указаны в CSSOM View Module.
Эксперимент без CSS Transform (работает)
Непонятный? Попробуйте следующий фрагмент JavaScript и HTML. При нажатии красная точка должна отображаться точно там, где произошел щелчок. Эта версия "довольно проста" и работает как ожидалось.
function click_handler(ev) {
var rect = this.getBoundingClientRect();
var left = ev.clientX - rect.left - this.clientLeft + this.scrollLeft;
var top = ev.clientY - rect.top - this.clientTop + this.scrollTop;
var dot = document.createElement('div');
dot.setAttribute('style', 'position:absolute; width: 2px; height: 2px; top: '+top+'px; left: '+left+'px; background: red;');
this.appendChild(dot);
}
document.getElementById("experiment").addEventListener('click', click_handler, false);
<div id="experiment" style="border: 5px inset #AAA; background: #CCC; height: 400px; position: relative; overflow: auto;">
<div style="width: 900px; height: 2px;"></div>
<div style="height: 900px; width: 2px;"></div>
</div>
Эксперимент с добавлением CSS-преобразования (он не работает)
Теперь попробуйте добавить CSS transform
:
#experiment {
transform: scale(0.5);
-moz-transform: scale(0.5);
-o-transform: scale(0.5);
-webkit-transform: scale(0.5);
/* Note that this is a very simple transformation. */
/* Remember to also think about more complex ones, as described below. */
}
Алгоритм не знает о преобразованиях и поэтому вычисляет неправильное положение. Что еще, результаты отличаются между Firefox 3.6 и Chrome 12. Opera 11.50 ведет себя точно так же, как Chrome.
В этом примере единственным преобразованием было масштабирование, поэтому я мог бы умножить коэффициент масштабирования для вычисления правильной координаты. Однако, если мы думаем о произвольных преобразованиях (масштабирование, поворот, перекос, перевод, матрица) и даже вложенные преобразования (преобразованный элемент внутри другого преобразованного элемента), то нам действительно нужен лучший способ вычисления координат.