Posted by Pavel Podlipensky on July 29 7:00 AM
<p>Сегодня я начинаю цикл статей посвященных фреймворку ExtJs. Это не будут туториалы или мануалы для пошагового изучения библиотеки. Я просто хочу поделиться своим опытом, поэтому начну с чего попало, а именно – с событий.</p> <p>С изобретением <a href="http://www.google.com.ua/url?q=http://en.wikipedia.org/wiki/Graphical_user_interface&sa=U&start=1&ei=PR9wSrvSFaKqnQO9tJipBw&sig2=3I9ncfaOyelm3jaaJS_ZNA&usg=AFQjCNEbgirDxOKmNayJ2SNpG-e2uMUXpA" target="_blank" rel="nofollow">GUI</a> и <a href="http://www.google.com.ua/url?q=http://ru.wikipedia.org/wiki/%25D0%259A%25D0%25BE%25D0%25BC%25D0%25BF%25D1%258C%25D1%258E%25D1%2582%25D0%25B5%25D1%2580%25D0%25BD%25D0%25B0%25D1%258F_%25D0%25BC%25D1%258B%25D1%2588%25D1%258C&sa=U&start=1&ei=VB9wSt7PL8HH_gbb05yhCQ&sig2=u9AxxRZayiLaLXwc0YLypQ&usg=AFQjCNGTfhbvuPK1M38xv4O07OAq30Xdyg" target="_blank" rel="nofollow">Мыши</a>, DOM-объекты, подобные кнопкам и текстовым полям стали обладать некими событиями. Например, <a href="http://www.w3.org/events.html#event-click">click</a>, <a href="http://www.w3.org/events.html#event-mouseover">mouseover</a>, <a href="http://www.w3.org/events.html#event-keydown">keydown</a> и другими. По сути событие – это сообщение, вызов функции, генерируемое одной частью приложения (источником), которое извещает другую часть приложения (приемник или подписчик) о том, что что-то случилось.</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; height: 42px; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #0000ff"><</span><span style="color: #800000">div</span> <span style="color: #ff0000">id</span><span style="color: #0000ff">="theDiv"</span> <span style="color: #ff0000">onclick</span><span style="color: #0000ff">="alert('You clicked me')"</span><span style="color: #0000ff">></span>Click me!<span style="color: #0000ff"></</span><span style="color: #800000">div</span><span style="color: #0000ff">></span></pre> <!--CRLF--></div> </div> <p>В ExtJs DOM-элементы оборачиваются в <a href="http://extjs.com/deploy/dev/docs/?class=Ext.Element">Ext.Element</a>, поэтому подписка на тоже событие будет выглядеть так:</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.39%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; height: 41px; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> Ext.get(<span style="color: #006080">'theDiv'</span>).on(<span style="color: #006080">'click'</span>, <span style="color: #0000ff">function</span>() {alert(<span style="color: #006080">'You clicked me'</span>);});</pre> <!--CRLF--></div> </div> <p>При разработке сложных пользовательских интерфейсов, отдельные элементы DOMа получают особое назначение. К примеру, банальный div может быть заголовком колонки в таблице (я имею ввиду <a href="http://extjs.com/playpen/ext-2.0/examples/grid/grid3.html" target="_blank" rel="nofollow">ExtJs Grid</a>). Следовательно, когда пользователь захочет изменить порядок колонок в гриде и начнет перетягивать колонку за заголовок – в заголовке произойдут некие события (mouseover, mousedown, click, …). Каждый раз отслеживать порядок и детали “низкоуровневых” событий в своем коде рутинно. Поэтому ExtJs поддерживает собственную модель событий. К примеру, <a href="http://extjs.com/deploy/dev/docs/output/Ext.grid.GridPanel.html" target="_blank" rel="nofollow">GridPanel</a> поддерживает такие события как <a href="http://extjs.com/source/GridPanel.html#event-Ext.grid.GridPanel-columnmove">columnmove</a>, <a href="http://extjs.com/source/GridPanel.html#event-Ext.grid.GridPanel-cellclick">cellclick</a>, <a href="http://extjs.com/source/GridPanel.html#event-Ext.grid.GridPanel-headerclick">headerclick</a> и другие.</p> <p>Вся магия событий в ExtJs реализована в классе <a href="http://extjs.com/deploy/dev/docs/?class=Ext.util.Observable">Ext.util.Observable</a>. Чтобы наделить ваш компонент свойствами источника события, вам необходимо “унаследоваться” от <a href="http://extjs.com/deploy/dev/docs/?class=Ext.util.Observable">Ext.util.Observable</a>.  Если же вы наследуете свой компонент от других компонентов, таких как Panel, Grid, Form, Tree и другие, то дополнительно наследоваться от <a href="http://extjs.com/deploy/dev/docs/?class=Ext.util.Observable">Ext.util.Observable</a> не обязательно. О принципе паттерна Observable и об особенностях его реализации на javascript можно прочесть на <a href="http://www.codeproject.com/KB/scripting/Observer_Pattern_JS.aspx" target="_blank" rel="nofollow">CodeProject</a>.</p> <p>После того как вы так или иначе унаследовались от <a href="http://extjs.com/deploy/dev/docs/?class=Ext.util.Observable">Ext.util.Observable</a>, вы можете добавить ваши собственные события примерно таким образом</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #0000ff">this</span>.addEvents( <span style="color: #006080">'IAmHappy'</span>, <span style="color: #006080">'IAmNotHappy'</span> );</pre> <!--CRLF--></div> </div> <p>Где, this – это и есть ваш компонент.</p> <p>Инициировать ваше событие также просто:</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #0000ff">this</span>.fireEvent(<span style="color: #006080">'IAmHappy'</span>, <span style="color: #0000ff">this</span>, firstArg, secondArg);</pre> <!--CRLF--></div> </div> <p>Когда мы вызываем функцию fireEvent, то Observable смотрит, подписывался ли на это событие (в данном случае – ‘IAmHappy’) кто-либо или нет. Если подписывался, то вызывает функцию-обработчик и устанавливает значение переменной this равное текущему объекту (этого мы добились указав this вторым параметром после названия события). </p> <p>Также в эту функцию будут переданы параметры event и target. Где, event будет объект типа <a href="http://extjs.com/source/EventManager.html#cls-Ext.EventObject">Ext.EventObject</a>, а target – ссылка на HTMLElement. Следом за этими параметрами пойдут наши, собственные параметры firstArg и secondArg, коих может быть любое количество.</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #0000ff">function</span> IAmHappyListener(<span style="color: #0000ff">event</span>, target, fristArg, secondArg){</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> ...</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> }</pre> <!--CRLF--></div> </div> <p>Если же Observable не нашел ни одного подписчика, то он, соответственно, ничего и не вызывает.</p> <p>Простейший способ подписаться на события ExtJs я показал выше, но существуют также некоторые вариации:</p> <p><em>Обработчик событий с задержкой </em>(обработчик запуститься через 250 миллисекунд после события)</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> el.on(<span style="color: #006080">'click'</span>, <span style="color: #0000ff">this</span>.onClick, <span style="color: #0000ff">this</span>, {delay: 250});</pre> <!--CRLF--></div> </div> <p><em>Обработчик событий с буферизацией </em>(это означает, обработчик запуститься не чаще, чем раз в 100 миллисекунд и при условии, что событие произошло)</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> el.on(<span style="color: #006080">'click'</span>, <span style="color: #0000ff">this</span>.onClick, <span style="color: #0000ff">this</span>, {buffer: 100});</pre> <!--CRLF--></div> </div> <p><em>Обработчик событий с условием распространения события</em></p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #008000">// prevent default</span></pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> el.on(<span style="color: #006080">'click'</span>, <span style="color: #0000ff">this</span>.onClick, <span style="color: #0000ff">this</span>, {preventDefault: <span style="color: #0000ff">true</span>});</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> </pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> <span style="color: #008000">// only stop propagation</span></pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum5" style="color: #606060"> 5:</span> el.on(<span style="color: #006080">'click'</span>, <span style="color: #0000ff">this</span>.onClick, <span style="color: #0000ff">this</span>, {stopPropagation: <span style="color: #0000ff">true</span>});</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum6" style="color: #606060"> 6:</span> </pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum7" style="color: #606060"> 7:</span> <span style="color: #008000">// prevent default and stop propagation</span></pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum8" style="color: #606060"> 8:</span> el.on(<span style="color: #006080">'click'</span>, <span style="color: #0000ff">this</span>.onClick, <span style="color: #0000ff">this</span>, {stopEvent: <span style="color: #0000ff">true</span>});</pre> <!--CRLF--></div> </div> <p><a href="https://developer.mozilla.org/en/DOM/event.preventDefault" target="_blank" rel="nofollow">preventDefault</a> и <a href="https://developer.mozilla.org/en/DOM/event.stopPropagation" target="_blank" rel="nofollow">stopPropagation</a> означают тоже самое, что и соответствующие методы родных, джаваскриптовых событий.</p> <p><em>Обработчик событий с другими опциями</em></p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> el.on(<span style="color: #006080">'click'</span>, <span style="color: #0000ff">this</span>.onClick, <span style="color: #0000ff">this</span>, {</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> single: <span style="color: #0000ff">true</span>, <span style="color: #008000">// удаляет автоматически, после первого запуска</span></pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> <span style="color: #0000ff">delegate</span>: <span style="color: #006080">'li.some-class'</span> <span style="color: #008000">// автоматическая делегация событий</span></pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> });</pre> <!--CRLF--></div> </div> <p>Добавление сразу нескольких обработчиков может выглядеть вот так:</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> el.on({</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> <span style="color: #006080">'click'</span> : {</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> fn: <span style="color: #0000ff">this</span>.onClick,</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> scope: <span style="color: #0000ff">this</span>,</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum5" style="color: #606060"> 5:</span> delay: 100</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum6" style="color: #606060"> 6:</span> }, </pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum7" style="color: #606060"> 7:</span> <span style="color: #006080">'mouseover'</span> : {</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum8" style="color: #606060"> 8:</span> fn: <span style="color: #0000ff">this</span>.onMouseOver,</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum9" style="color: #606060"> 9:</span> scope: <span style="color: #0000ff">this</span></pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum10" style="color: #606060"> 10:</span> },</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum11" style="color: #606060"> 11:</span> <span style="color: #006080">'mouseout'</span> : {</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum12" style="color: #606060"> 12:</span> fn: <span style="color: #0000ff">this</span>.onMouseOut,</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum13" style="color: #606060"> 13:</span> scope: <span style="color: #0000ff">this</span></pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum14" style="color: #606060"> 14:</span> }</pre> <!--CRLF--> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum15" style="color: #606060"> 15:</span> });</pre> <!--CRLF--></div> </div> <p>Еще стоит упомянуть о такой полезной функции как <a href="http://extjs.com/deploy/dev/docs/source/Observable-more.html#method-Ext.util.Observable-relayEvents" target="_blank" rel="nofollow">relayEvents</a>. Она позволяет делегировать события одного компонента – другому компоненту. К примеру, у вас есть панель A, содержащая другую панель B, а та в свою очередь содержит кнопку Save. По нажатию на кнопку Save произойдет ваше собственное событие onSomeSave. К сожалению, ни панель А, ни панель B, обработать это событие не может. Поэтому панели А необходимо генерировать это событие как свое собственное, в надежде, что кто-то сможет его обработать.</p> <p><a href="http://podlipensky.com/image.axd?picture=WindowsLiveWriter/ExtJS/6603A090/clip_image001.gif"><img title="clip_image001" style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" height="222" alt="clip_image001" src="http://podlipensky.com/image.axd?picture=WindowsLiveWriter/ExtJS/0F0E3F8D/clip_image001_thumb.gif" width="331" border="0" /></a></p> <p>“Передать” события панели B в панель A и позволяет relayEvents:</p> <div id="codeSnippetWrapper" style="border-right: silver 1px solid; padding-right: 4px; border-top: silver 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: silver 1px solid; width: 97.5%; cursor: text; direction: ltr; line-height: 12pt; padding-top: 4px; border-bottom: silver 1px solid; font-family: 'Courier New', courier, monospace; background-color: #f4f4f4; text-align: left; max-height: 200px"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"> <pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> panelA.relayEvents(panelB, [<span style="color: #006080">'onSomeSave'</span>]);</pre> <!--CRLF--></div> </div> <p>Фух, что-то длинный пост получился. Устал. А я ведь еще не рассказал об <a href="http://extjs.com/deploy/dev/docs/source/EventManager.html#cls-Ext.EventManager" target="_blank" rel="nofollow">Ext.EventManager</a>, о такой полезной функции как <a href="http://extjs.com/deploy/dev/docs/source/EventManager.html#method-Ext.EventObject-within" target="_blank" rel="nofollow">within</a> и о многом другом. Ну чтож, это в другой раз.</p> <p><strong>Полезные материалы.</strong></p> <div id="__ss_153232" style="width: 425px; text-align: left"><a title="Ext Js Events" style="display: block; margin: 12px 0px 3px; font: 14px helvetica,arial,sans-serif; text-decoration: underline" href="http://www.slideshare.net/sdhjl2000/ext-js-events-153232">Ext Js Events</a><object style="margin:0px" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=ext-js-events-1193984087101149-2&rel=0&stripped_title=ext-js-events-153232" /><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=ext-js-events-1193984087101149-2&rel=0&stripped_title=ext-js-events-153232" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object> <p></p> </div> <p><strong>Полезные ссылки</strong></p> <p><a href="http://www.w3.org/TR/DOM-Level-3-Events/events.html" target="_blank" rel="nofollow">Document Object Model Events</a></p> <p><a href="http://www.quirksmode.org/js/introevents.html" target="_blank" rel="nofollow">Introduction to Events</a></p> <p><a href="http://extjs.com/learn/Tutorial:Events_Explained" target="_blank" rel="nofollow">Tutorial:Events Explained</a></p> <p><a href="http://extjs.com/learn/Tutorial:RelayEvents" target="_blank" rel="nofollow">Tutorial:RelayEvents</a></p>

Сегодня я начинаю цикл статей посвященных фреймворку ExtJs. Это не будут туториалы или мануалы для пошагового изучения библиотеки. Я просто хочу поделиться своим опытом, поэтому начну с чего попало, а именно – с событий.

С изобретением GUI и Мыши, DOM-объекты, подобные кнопкам и текстовым полям стали обладать некими событиями. Например, click, mouseover, keydown и другими. По сути событие – это сообщение, вызов функции, генерируемое одной частью приложения (источником), которое извещает другую часть приложения (приемник или подписчик) о том, что что-то случилось.

   1: <div id="theDiv" onclick="alert('You clicked me')">Click me!</div>

В ExtJs DOM-элементы оборачиваются в Ext.Element, поэтому подписка на тоже событие будет выглядеть так:

   1: Ext.get('theDiv').on('click', function() {alert('You clicked me');});

При разработке сложных пользовательских интерфейсов, отдельные элементы DOMа получают особое назначение. К примеру, банальный div может быть заголовком колонки в таблице (я имею ввиду ExtJs Grid). Следовательно, когда пользователь захочет изменить порядок колонок в гриде и начнет перетягивать колонку за заголовок – в заголовке произойдут некие события (mouseover, mousedown, click, …). Каждый раз отслеживать порядок и детали “низкоуровневых” событий в своем коде рутинно. Поэтому ExtJs поддерживает собственную модель событий. К примеру, GridPanel поддерживает такие события как columnmove, cellclick, headerclick и другие.

Вся магия событий в ExtJs реализована в классе Ext.util.Observable. Чтобы наделить ваш компонент свойствами источника события, вам необходимо “унаследоваться” от Ext.util.Observable.  Если же вы наследуете свой компонент от других компонентов, таких как Panel, Grid, Form, Tree и другие, то дополнительно наследоваться от Ext.util.Observable не обязательно. О принципе паттерна Observable и об особенностях его реализации на javascript можно прочесть на CodeProject.

После того как вы так или иначе унаследовались от Ext.util.Observable, вы можете добавить ваши собственные события примерно таким образом

   1: this.addEvents( 'IAmHappy', 'IAmNotHappy' );

Где, this – это и есть ваш компонент.

Инициировать ваше событие также просто:

   1: this.fireEvent('IAmHappy', this, firstArg, secondArg);

Когда мы вызываем функцию fireEvent, то Observable смотрит, подписывался ли на это событие (в данном случае – ‘IAmHappy’) кто-либо или нет. Если подписывался, то вызывает функцию-обработчик и устанавливает значение переменной this равное текущему объекту (этого мы добились указав this вторым параметром после названия события).

Также в эту функцию будут переданы параметры event и target. Где, event будет объект типа Ext.EventObject, а target – ссылка на HTMLElement. Следом за этими параметрами пойдут наши, собственные параметры firstArg и secondArg, коих может быть любое количество.

   1: function IAmHappyListener(event, target, fristArg, secondArg){
   2: ...
   3: }

Если же Observable не нашел ни одного подписчика, то он, соответственно, ничего и не вызывает.

Простейший способ подписаться на события ExtJs я показал выше, но существуют также некоторые вариации:

Обработчик событий с задержкой (обработчик запуститься через 250 миллисекунд после события)

   1: el.on('click', this.onClick, this, {delay: 250});

Обработчик событий с буферизацией (это означает, обработчик запуститься не чаще, чем раз в 100 миллисекунд и при условии, что событие произошло)

   1: el.on('click', this.onClick, this, {buffer: 100});

Обработчик событий с условием распространения события

   1: // prevent default
   2: el.on('click', this.onClick, this, {preventDefault: true});
   3:  
   4: // only stop propagation
   5: el.on('click', this.onClick, this, {stopPropagation: true});
   6:  
   7: // prevent default and stop propagation
   8: el.on('click', this.onClick, this, {stopEvent: true});

preventDefault и stopPropagation означают тоже самое, что и соответствующие методы родных, джаваскриптовых событий.

Обработчик событий с другими опциями

   1: el.on('click', this.onClick, this, {
   2:     single: true, // удаляет автоматически, после первого запуска
   3:     delegate: 'li.some-class' // автоматическая делегация событий
   4: });

Добавление сразу нескольких обработчиков может выглядеть вот так:

   1: el.on({
   2:     'click' : {
   3:         fn: this.onClick,
   4:     scope: this,
   5:     delay: 100
   6:     }, 
   7:     'mouseover' : {
   8:         fn: this.onMouseOver,
   9:     scope: this
  10:     },
  11:     'mouseout' : {
  12:         fn: this.onMouseOut,
  13:         scope: this
  14:     }
  15: });

Еще стоит упомянуть о такой полезной функции как relayEvents. Она позволяет делегировать события одного компонента – другому компоненту. К примеру, у вас есть панель A, содержащая другую панель B, а та в свою очередь содержит кнопку Save. По нажатию на кнопку Save произойдет ваше собственное событие onSomeSave. К сожалению, ни панель А, ни панель B, обработать это событие не может. Поэтому панели А необходимо генерировать это событие как свое собственное, в надежде, что кто-то сможет его обработать.

clip_image001

“Передать” события панели B в панель A и позволяет relayEvents:

   1: panelA.relayEvents(panelB, ['onSomeSave']);

Фух, что-то длинный пост получился. Устал. А я ведь еще не рассказал об Ext.EventManager, о такой полезной функции как within и о многом другом. Ну чтож, это в другой раз.

Полезные материалы.

Полезные ссылки

Document Object Model Events

Introduction to Events

Tutorial:Events Explained

Tutorial:RelayEvents

blog comments powered by Disqus