Меню

Първи стъпки в правене на сайтове

Влез Излез

Първи стъпки в JavaScript – Събития/Events

Достигаме и до шеста част от уводната поредица в JavaScript, в която ще обърнем внимание на събитията (event-и). Благодарение на събитията в JavaScript имаме възможност да извършим дадено действие когато потребител извърши специално действие или когато някой от обектите съобщи, че „си е свършил работата“.

Event-ите могат да се използват за интеракция и въвеждане на повече динамика в интернет страниците, които правим, като благодарение на тях можем да извършим някаква инициализация, действия в момента в който цялото съдържание на страницата (включително картинки и видео) са заредени, извикване на функция в отговор на действие на потребителя, породено от неговата мишка (клик, или просто преминаване на мишката над някой елемент) и много други.

Event-ите в JavaScript са едно от най-важните и често използваните неща в езика. За съжаление все още съществуват различия в начина, по който различните браузъри ги обработват. В този урок ще представим как би трябвало да работят събитията в JavaScript, според официалната спецификация на езика, а относно как да процедиаме с браузъри като Internet Explorer ще говорим в по-нататъшните уроци, в които ще представим реални случаи на употреба на JavaScript в браузърите, при които ще използваме библиотеката jQuery, която се грижи кодът ни да се изпълнява по еднакъв начин на множество браузъри, които библиотеката поддържа.

Един от най-лесните, но и като цяло не особено добър начин да се извикат функции при появата на даден event е като се използва следния синтаксис:

function doSomething() {
	// тяло на функцията
}

window.onload = doSomething;

Така първо дефинираме функцията doSomething(), след което я присвояваме на onload property-то на обекта window. При пълното зареждане на страницата обектът window хвърля load event, който кара самият обект да изпълни своето property onload в случай, че то е функция.

Проблемът в този ходход е, че можем да изпълним една единствена функция при зареждането на страницата.

По-правилен начин, който позволява да асоцииране няколко handler функции при един и същи event е следния:

function doSomething() {
	// тяло на функцията
}

function doSomethingElse() {
	// тяло на втората функция
}

window.addEventListener('load', doSomething, true);
window.addEventListener('load', doSomethingElse, true);

Самият метод addEventListener() приема три аргумента: type (тип на събитието), listener (функция, която да се изпълни) и useCapture – булева стойност (true/false), която по подрадбиране е false. Capture е фазата от събитието, при която се влиза навътре към елементите. Ако имаме един <div> и вътре в него <a> можем да си представим div-а като една голяма кутия, в която има по-малка (а-то). За да кликнем на а-то, първо трябва да кликнем на div-а, да влезем в него и тогава ще достигнем до а-то. Този процес на навлизане от външните към вътрешните елементи се нарича Capture. След като това е преминало започва Bubble фазата на събитието, която се изпълнява в обратния ред – отвътре-навън. Ако при задаване на event listener използваме useCapture нашата функция ще се извърши по време на първата фаза, преди да е достигнала до пълната дълбочина на елементите.

При задаване на event handler – функцията, която определяме да се изпълни при самото събитие получава аргумент, който е обект на събитието. Този обект съдържа информация за типа на събитието, кой е най-вътрешният елемент, който го е предицвикал, кой е текущият елемент, на който всъщност сме задали 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 – можем да задаваме каквото име си поискаме, стига да не влиза в конфликт със запазена дума в JavaScript), която показва два popup-а. Първият popup дава информация за target обекта на събитието (това е най-вътрешният обект, който е предизвикал събитието – в случая това е същинският обект, на който сме кликнали), а във втория дава информация за currentTarget обекта, който е обекта към който сме задали event listener.

Чрез 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

Въпреки, че няма да отделим много време на това сега искам да спомена, че при IE този код няма да работи. Това е поради факта, че IE изпълзва друг метод за добавяне на event listener-и. Неговият синтаксис е:

element.attachEvent(type, listener);

Трябва да се отбележи, че при IE типът на събитието съдържа представка „on“, така че „load“ става „onload“, „click“ става „onclick“ и т.н. – типът на събитията е като property-тата на елементите от първия начин, по който показахме, че може да се зададе event listener.

Освен това при IE нямаме useCapture и доколкото ми е известно, събитиято винаги се обработват по време на bubble фазата. Също така в IE нямъме target и currentTarget. target има еквивалент – e.srcElement, но за currentTarget доколкото ми е известно на мене – няма еквивалент.

Защо има разлика в JavaScript при IE и останалите браузъри?

Тук идва ред за малко история – преди около 15-20 години е имало само два браузъра за Windows – Internet Explorer, разработван от Microsoft и Netscape Navigator, разработван от AOL. Войната между браузърите тогава е била доста по-ужесточена от това, което наблюдаваме в наши дни и browser vendor-ите са се опитвали всячески да докажат, че техния браузър е по-добър. Първоначално Netscape добавят интерактивни възможности към страниците посредством JavaScript, в отговор на което, Microsoft добавят поддръжка за JScript в Internet Explorer. Забележете, че официално това не е JavaScript, а друг език, въпреки че той се добавя в HTML не само с <jscript> таг, но и със <script> таг, който от самото начало е тагът за добавяне на JavaScript. По-голямата част от синтаксиса на JScript повтаря тази на JavaScript, но разбира се има различия – например addEventListener и attachEvent, които разглеждаме в този урок. С развитието на JavaScript и добавяне на нови възможности, Microsoft добавят вече всичко кажи-речи 1:1 в JScript, но все още си държат на различията, които датират от едно време. Дори IE9 не поддържа addEventListener.

Едно от най-добрите места за справка относно JavaScript (истинският JavaScript) можете да правите в Mozilla Developer Network. Mozilla, както може би знаете, са разработчиците на браузъра Firefox, който е официалният наследник на Netscape, след като AOL обявиха спирането на поддръжката му (дори много от хората, които работят в Mozilla са същите хора, които на времето са разработвали Netscape). Тъй като именно тези хора са създали езика JavaScript, можем да се доверим на написаното от тях. Редно е все пак да се отбележи, че от известно време насам (не съм сигурен за коя година става въпрос, затова няма да се опитвам да гадая) спецификацията на JavaScript вече не се определя от Mozilla, а от W3C, така че макар и Mozilla да са създали JavaScript, функционалностите, правилният синтаксис и прочие, вече се диктуват от World Wide Web Consortium. Така че за последните спецификации на JavaScript можете да потърсите в Google за: w3c javascript specification. Не се надявайте обаче да откриете едно място с информация за всичко в JavaScript, тъй като езикът всъщност е доста богат, а подобна цялостна спецификация би било немислимо да се съхранява на едно единствено място. Затова спецификацията е разделена на части. Относно event listener-ите можете да погледнете Document Object Model (DOM) Level 2 Events Specification и Document Object Model (DOM) Level 3 Events Specification, въпреки че четенето на суха спецификация може би не е най-уместно за начинаещи.

Вашият коментар

Вашият имейл адрес няма да бъде публикуван. Задължителните полета са отбелязани с *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax