
Първи стъпки в JavaScript – Събития (events)
Достигаме и до шеста част от уводната поредица в JavaScript. Тук ще обърнем внимание на събитията (event-и). Благодарение на събитията можем да изпълним код в ключов момент. Той може да е когато потребител извърши нещо конкретно. Може да бъде и когато друг обект извести, че е „готов“ с дадена функционалност
Event-ите могат да се използват за интеракция и повече динамика в страниците. Благодарение на тях можем да извършим някаква инициализация, когато страницата е заредена.
Event-ите в JavaScript са едно от най-важните и често използваните неща в езика.
Един от най-лесните, но неправилни начини е със следния синтаксис:
function doSomething() {
// тяло на функцията
}
window.onload = doSomething;
Първо дефинираме функцията doSomething()
. След това я присвояваме на onload
property-то на обекта window
. При пълното зареждане на страницата обектът window
хвърля load
event. Той кара самият обект да изпълни своето property onload
в случай, че то е функция.
Проблемът в този подход е, че можем да изпълним една единствена функция при зареждането на страницата.
addEventListener
По-правилен начин, който позволява да асоциирате няколко handler функции при един и същи event е следния:
function doSomething() {
// тяло на функцията
}
function doSomethingElse() {
// тяло на втората функция
}
window.addEventListener('load', doSomething, true);
window.addEventListener('load', doSomethingElse, true);
Самият метод addEventListener()
приема три аргумента:
type
– тип на събитиетоlistener
– функция, която да се изпълниuseCapture
– булева стойност (true/false), която по подразбиране еfalse
.
Фази на събитията
Capture
Capture е фазата от събитието, при която се влиза навътре към елементите.
Нека разгледаме елементите като кутии, в които можем да имаме други такива.
Следната структура:
<div>
<ul>
<li>
<a href="#">Click</a>
</li>
</ul>
</div>
може да се оприличи на 4 матрьошки.
Ако се опитаме да кликнем на линка най-вътре, първо трябва мишката да „мине“ през по-външните елементи. Даже първо ще мине през най-външния.
Ако useCapture
е true
, функцията ще се изпълни още при click
на div
-а, след това на ul
, после на li
и накрая на a
.
Bubble
След като стигнем до най-вътрешния елемент, започва bubble
фазата. Нарича се така, защото върви наобратно – от най-вътрешния към най-външния елемент.
По подразбиране събитията се изпълняват в този ред. И ако имаме клик на линка ще можем първо да обработим него. Чак след това ще дойде li
, после ul
и накрая div
.
Обект на събитието
Функциите, които се изпълняват при събитие, автоматично получават аргумент обекта на събитието. Този обект съдържа информация за:
- типа на събитието
- кой е най-вътрешният елемент, който го е предизвикал
- кой е текущият елемент, на който всъщност сме задали handler-а
- множество друга информация, в зависимост от това какво е точно събитието, което обработваме.
function eventInfo(e) {
alert('target: ' + e.target.nodeName);
alert('currentTarget: ' + e.currentTarget.nodeName);
}
document.getElementById('div').addEventListener('click', eventInfo);
document.getElementById('a').addEventListener('click', eventInfo);
В този пример декларираме функцията eventInfo()
.
Тя приема аргумент e
. Това е съкратено от event
. Аргументът във функцията можем да наименоваме различно. Аз лично съм свикнал винаги да ползвам e
(като конвенция).
target и currentTarget
Функцията показва 2 съобщения. Първото дава информация за target
обекта на събитието.
target
е най-вътрешният елемент, който е предизвикал събитието. При click
събития, това е елементът, който реално сме кликнали.
Във второто съобщение показваме currentTarget
обекта. Това е обектът, към който сме задали event listener.
В единия случай от примера и двете съобщения ще се отнасят за link-а. В другия обаче ще върнем информация за link-а като target
, а после и div
-а като currentTarget
. Това, защото сме задали addEventListener
на div
-а (затова currentTarget
).
Чрез document.getElementById(
id
)
достъпваме елемент от страницата, която сме заредили. Като аргумент на функцията подаваме string с ID-то на елемента, който искаме да селектираме. В случая се предполага, че имаме div с id="div"
и a с id="a"
. При клик и на двата елемента извикваме една и съща функция – eventInfo()
.
В случая не използваме useCapture
, което означава, че listener-ите ще се изпълнят първо за А
-тага, а след това за DIV
-а.
target
-а на A
-тагът е самият A
таг, затова e.target.nodeName
има стойност A
.
currentTarget
обектът на събитието е същият елемент, тъй като event listener-ът е зададен за този таг.
След като събитието е обработено то продължава към parent елемента (DIV
-ът) през своята bubble фаза.
На нов ред се извиква функцията. target
елементът е A
-тагът, но този път вече currentTarget
е DIV
-ът.
Резултат: popup-и със следните надписи:
- target: A
- currentTarget: A
- target: A
- currentTarget: DIV
Ако бяхме задали useCapture = true
за двата event listener-а (всъщност е достатъчно и само за DIV-ът), тогава първо щеше да се изпълни неговият event listener, но target-а щеше да си остане А-то. Резултатът щяха да са popup-и със следните надписи:
- target: A
- currentTarget: DIV
- target: A
- currentTarget: A