
Първи стъпки в 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