📜 ⬆️ ⬇️

Declarative event handlers on a web page

Working closely with the Backbase AJAX framework, imbued with the convenience of using declarative event handlers when the handler is defined in the place where it is used.
Here is an example of a click event handler in a button:
< button >
< tb:handler event ='click' >
alert(' click ')
</ tb:handler >
</ button >


* This source code was highlighted with Source Code Highlighter .
I will briefly describe the basis for implementation. To declare a handler, we use a non-standard tag in our namespace (for correct operation in xhtml). On the loading page we find all our elements and create real event handlers. In addition to ease of use, this approach allows you to expand the capabilities of handlers and to unify work in different browsers, for example, event handling in IE, or bring keyboard events to one denominator.

The first version demonstrating the main ideas of implementation:
if (!window.ev){
// ,
window.ev = {};
//
ev.namespaceURI = 'http://tedbeer.net' ;
// ,
ev.browser = {ie : false , standard : false };
ev.browser.standard = Boolean( document .addEventListener);
ev.browser.ie = '\v' == 'v' ; //for other browsers it's a vertical tab - \u000B

//
ev.init = function ( event ){
var arr = [];
if (ev.browser.ie) {
arr = document .getElementsByTagName( 'handler' );
for ( var i = 0; i < arr.length; i++) {
if (arr[i].tagUrn == ev.namespaceURI) {
arr[i].parentNode[ 'on' + arr[i].getAttribute( 'event' )] =
new Function( 'event' , arr[i].innerText);
arr[i].innerText = '' ; //clean up to avoid displaying
}
}
} else {
arr = document .getElementsByTagNameNS(ev.namespaceURI, 'handler' ); //xhtml
if (!arr.length) //html
arr = document .getElementsByTagName( 'tb:handler' );
for ( var i = 0; i < arr.length; i++) {
arr[i].parentNode.addEventListener( arr[i].getAttribute( 'event' ),
new Function( 'event' , arr[i].textContent), false );
arr[i].textContent = '' ; //clean up to avoid displaying
}
}
};

if (ev.browser.standard)
window.addEventListener( 'load' , ev.init, false );
else
window.onload = ev.init;
}


* This source code was highlighted with Source Code Highlighter .

Test page:
< style type ="text/css" >
.test-div {
width: 200px;
height: 200px;
background-color: #6699FF;
}
</ style >
< script type ="text/javascript" src ="ev.js" ></ script >
< div class ="test-div" >
< tb:handler event ='click' >
alert('click');
</ tb:handler >
</ div >

* This source code was highlighted with Source Code Highlighter .

online test code ev.js

The code is transparent enough to easily understand what it is doing, but there are some, consciously admitted, jambs in it:
Competent developers will find more shoals, leave it as a homework :-), so that there is something to write in the comments. In the following code, we will fix these jambs and add the following features:The code becomes a little more complicated:
if (!window.ev){
// ,
window.ev = {};
//
ev.namespaceURI = 'http://tedbeer.net' ;
// ,
ev.browser = {ie : false , standard : false };
ev.browser.standard = Boolean( document .addEventListener);
ev.browser.ie = '\v' == 'v' ; //for other browsers it's a vertical tab - \u000B

//
ev.setEventHandler = function (oNode, oEventNode){
if ( this .browser.ie) {
var vHandler = function (){
var self = arguments.callee;
var ev = window. event ;
ev.target = ev.srcElement;
ev.currentTarget = self.node.parentNode;
var sMatch = self.node.getAttribute( 'match' );
if (sMatch === null || Sizzle.matches(sMatch, [ev.target]).length > 0)
return self.handler(ev);
}
vHandler.node = oEventNode;
var sBody = oEventNode.innerText;
if (!sBody.length) { // CDATA (xhtml)
for ( var i = 0; i < oEventNode.childNodes.length; i++)
if (oEventNode.childNodes[i].nodeType) { //4 or 8 - CDATA or comment
sBody = oEventNode.childNodes[i].nodeValue;
break ; //
}
} else //
oEventNode.innerText = '' ;
//
try {
vHandler.handler = new Function( 'event' , sBody);
} catch (e) { //
alert(e.message + ':\n' + sBody)
}
oNode.attachEvent( 'on' + oEventNode.getAttribute( 'event' ), vHandler);
} else {
var vHandler = function ( event ){
var self = arguments.callee;
var sMatch = self.node.getAttribute( 'match' );
if (sMatch === null || Sizzle.matches(sMatch, [ event .target]).length > 0)
return self.handler( event );
}
vHandler.node = oEventNode;
var sBody = oEventNode.textContent;
if (!sBody.length) { // CDATA (xhtml)
for ( var i = 0; i < oEventNode.childNodes.length; i++) {
if (oEventNode.childNodes[i].nodeType) { //4 or 8 - CDATA or comment
sBody = oEventNode.childNodes[i].nodeValue;
break ; //
}
}
} else //
oEventNode.textContent = '' ;
//
try {
vHandler.handler = new Function( 'event' , sBody);
} catch (e) { //
alert(e.message + ':\n' + sBody)
}
oNode.addEventListener( oEventNode.getAttribute( 'event' ), vHandler, false );
}
};

ev.init = function ( event ){
var arr = [];
if ( document .getElementsByTagNameNS) { //xhtml
arr = document .getElementsByTagNameNS(ev.namespaceURI, 'handler' )
}
if (!arr.length) {
if (ev.browser.ie) { //ie
arr = []; //convert a nodelist to array
var arr2 = document .getElementsByTagName( 'handler' );
for ( var i = 0; i < arr2.length; i++) {
if (arr2[i].tagUrn == ev.namespaceURI) { //accept our namespace only
arr.push(arr2[i]);
}
}
} else //html
arr = document .getElementsByTagName( 'tb:handler' );
}
for ( var i = 0; i < arr.length; i++) {
ev.setEventHandler(arr[i].parentNode, arr[i]);
}
};
if (ev.browser.standard) {
window.addEventListener( 'load' , ev.init, false );
} else
window.attachEvent( 'onload' , ev.init);
}


* This source code was highlighted with Source Code Highlighter .
Test page:
.test-div {
width: 200px;
height: 200px;
background-color: #6699FF;
border-bottom: 1px dotted #666666;
}
.inner-div {
width: 100px;
height: 100px;
background-color: #66CCCC;
}
.test-div2 {
width: 200px;
height: 200px;
background-color: #6699FF;
}
.inner-div2 {
width: 100px;
height: 100px;
background-color: #66CCCC;
}
</ style >
< script type ="text/javascript" src ="sizzle.js" ></ script >
<script type= "text/javascript" src= "ev2.js" > </ script >
</ head >
< body >
Click on boxes to see what event handlers are called.
< div class ="test-div" >
< tb:handler event ='click' match ='.inner-div' ><!--
alert('A: ' + event.target.className)
// comment
alert('<A2>')
--></ tb:handler >
< tb:handler event ='click' match ='.test-div' ><!--
alert('B: ' + event.target.className)
// comment
alert('<B2>
')
--></ tb:handler >
< div class ="inner-div" > XXX </ div >
</ div >
< div class ="test-div2" >
< tb:handler event ='click' match ='.inner-div2' >
alert('C: ' + event.target.className)
</ tb:handler >
< div class ="inner-div2" > YYY </ div >
</ div >


* This source code was highlighted with Source Code Highlighter .

online html test xhtml test code ev2.js
The code is inside the comment to avoid browser parsing and save spaces and line breaks. For xhtml, put the code in the CDATA section.
Notice the use of the sizzle library for working CSS selectors. Similarly, you can add your own event handler extensions.
')
Conclusion :
The code above is just a small demonstration. If you are looking for a good framework for serious projects and you are not denied declarative languages, I recommend looking at Backbase and AmpleSDK . Actually, one person has worked on both frameworks. About the latter ( AmpleSDK ) there should soon be an announcement on ayaksin . The author is still amazed at his rights with the old contract and cannot open the source code. But as soon as the deadline passes (soon), I promised to make the framework open source. The framework implements many other modules (svg, smil, xinclude, schema ...), all this cross-browser (including IE) and, most importantly, uses the standard API, and not homegrown interfaces. Therefore, it is easy to start using - just look through the standard.

Shl . The code can be used without restrictions for any of their projects. It is not copied from anywhere. I basically did not look into the implementation of the available frameworks to avoid this.
UPD . Here is the announcement AmpleSDK ripened: ajaxian.com/archives/ample-sdk-browser-in-a-browser

Source: https://habr.com/ru/post/61734/


All Articles