Ereignisse (Events) Was ist das?

Für die Registrierung von Event-Handlern gibt es browserabhängige, inkompatible Modelle, wie z.B. inline, traditional, firmespezifische Lösungen und W3C. Hier wird vorrangig das Modell von W3C: UI-Events , Mozilla: Web-API-Event , Whatwg: DOM-Event , W3C: Overview of the DOM Level 2 Event Model betrachtet.

Events (allgemein) Ereignis lösen funktionen aus ...
Eingabegeräte, wie Maus oder Tastatur ermöglichen Tastenanschläge, Maus-Bewegungen und Maus-Klicks. Dadurch werden Ereignisse generiert, die das Sytem verwaltet und verteilt (Callback-Funktionen). Eine Grafische Benutzeroberfläche (Benutzerschnittstelle, Interaktionen) verwendet solche Events. Siehe z.B. de.wikipedia: XML_Events , en.wikipedia: DOM_events , quirksmode: events
Sytemgenerierte Ereignise ('load', 'resize', 'unload', 'scroll') Seite geladen/beenden/size ändert/gescollt?

Auf ein HTML-Element kann nur dann zugegriffen werden, wenn es geladen ist. window.onload wird aufgerufen (sytemgeneriertes Ereignis), wenn eine Seite fertig geladen ist. Siehe z.B. w3schools HTML Event Attributes .

Beispiele für window-Events:
onafterprint, onbeforeprint, onbeforeunload, onerror, onhaschange, onload, onmessage, onoffline, ononline, onpagehide, onpageshow, onpopstate, onredo, onresize, onstorage, onundo, onunload

Beispiele für form-Events:
onblur, onchange, oncontextmenu, onfocus, onformchange, onforminput, oninput, oninvalid, onselect, onsubmit

Für EventListeners kann verwendet werden:

 
if( window.addEventListener ) {          window.addEventListener('load',handlerFunction,false);
} else if( document.addEventListener ) { document.addEventListener('load',handlerFunction,false);
}

onunload 
Beispiel: obj.addEventListener / obj.attachEvent window.onload

Sollen fn1() und fn2() nach dem Laden einer Seite aufgerufen werden, so kann das window.onload - Ereignis verwendet werden.

window.onload = function() {
  fn1();
  fn2();
}

Um zu zeigen, wie obj.addEventListener / obj.attachEvent verwendet werden können, soll mit Hilfe der folgenden Funktion addEvent(obj, evType, fn) das onload - Ereignis in einer alternativen Art verwenden werden. Alternative:

function addEvent(obj, evType, fn){ 
  var r;
  if (obj.addEventListener) { // w3C
    obj.addEventListener(evType, fn, false); 
    return true; 
  } else if (obj.attachEvent){ // IE < 9
    r = obj.attachEvent("on"+evType, fn); 
    return r; 
  } else { 
    return false; 
  } 
}

function fn1() { return this; }
var fn2 = function() { return this; }
// Aufruf
addEvent(window, 'load', fn1);
addEvent(window, 'load', fn2);
Elementinterne Ereignisfunktionen ...

Ein HTML-Element muß eine 'handleEvent'-Methode besitzen um Ereignisse (auf diesem Element) in einfachster Form zu behandeln. Solche Ereignisse sind z.B. 'abort', 'blur', 'change', 'focus', 'select', 'submit', 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup'

Manche Elemente haben eine Default-Ereignis-Steuerung. Beispiele:

Code Snippet: a-Elemente mit onclick-Ereignis und Default-Ereignis-Steuerung
  1. <a id="1" tabindex="20"
  2.    title="das orginal"
  3.    href="javascript:void(0)"
  4.    onclick="alert(this.title);return false;">
  5.    20 alert(this.title), 20 hat id="my_tag"
  6.    </a>
  7.  
  8. <a tabindex="21" href="javascript:void(0)"
  9.    onclick="alert(document.getElementsByTagName('pre')[0].innerHTML);return false;">
  10.    21 alle a-Tags sind im pre-Tag, debug pre-tag zeigt Source in alert-box
  11. </a>
  12.  
  13. <a tabindex="22" href="javascript:void(0)"
  14.    onclick="var txt='30 schrieb';document.getElementById('my_tag').title=txt;return false;">
  15.    22 schreibt String ins title-Attribut vom Tag mit id="my_tag"
  16. </a>
  17.  
  18. <a tabindex="22" href="javascript:void(0)"
  19.    onclick="var txt='40 schrieb';document.getElementById('my_tag').title=txt;return false;">
  20.    23 schreibt String ins title-Attribut vom Tag mit id="my_tag"
  21. </a>
  22.  
  23. <a tabindex="23" href="#my_tag"
  24.    onclick="document.getElementById('my_tag').focus();return true;">
  25.    24 springe per Klick innerhalb dieser Seite zum Tag mit id="my_tag"
  26. </a>
  27.  
  28. <a tabindex="24" href="javascript:alert(document.getElementById('my_tag').title);">
  29.    25 hole title-String aus dem Tag mit id="my_tag"
  30. </a>
DOM-Walk (Bsp: getElementsByAttribute) leider keine DOM-Funktion ...

Zu Ereignisses gehören Targets, die meist zu einem DOM-Baum (z.B.XHTML-Nodes) gehören.

Die folgende Funktion dom erzeugt ein ECMAScript-Objekt, mit den Funktionen root und getElementsByAttribute. Infolge der Rekursivität können ab dem root-Knoten alle nodes durchlaufen und alle jene nodes aufgesammelt werden, die das gewünschte Attribut haben.

Code Snippet: getElementsByAttribute() nach Crockford
  1. var dom = (function() {
  2.   var root_node = document.body,
  3.   
  4.   get_childs = function walk(node, fn) {
  5.    fn(node);
  6.    node = node.firstChild;
  7.    while(node) {
  8.      walk(node, fn);
  9.      node = node.nextSibling;
  10.    }
  11.   };
  12.   return {
  13.   getElementsByAttribute: function(att, val){ var r = [];
  14.     get_childs(root_node, function(node) {
  15.      var akt = node.nodeType === 1 && node.getAttribute(att);
  16.      if(typeof akt === 'string' && (akt === val || typeof val !== 'string')) {
  17.         r.push(node);
  18.      }
  19.    }); return r;
  20. },
  21. root: function(node) { root_node = node || document.body; return this;}
  22. };}());
dom.getElementsByAttribute(att)

Test p-tag (#eeefff) mit span-tag (#fffeee)

löschen der Anzeige
dom.root(document.getElementById('id_src')) .getElementsByAttribute('title');
dom.root(document.getElementById('id_src')) .getElementsByAttribute('style');
dom.root(document.getElementsByTagname('body')[0]) .getElementsByAttribute('id');
Anzeige:
W3C DOM-Level-2-Events (allgemein) ...

W3C: HTML event types W3C: DOM-Level-2-Events (Interface MutationEvent )

DOM 2 Events berücksichtigen unterschiedliche Möglichkeiten, welches Element (und wie) auf Ereignisse reagieren können. Ein Element el muß eine 'handleEvent'-Methode besitzen. Für die Weitergabe von Ereignissen (Propagation von Methoden) dient die eventObject.eventPhase (Capture- und Bubble-Phase).

 // W3C:
 el.addEventListener('nameOfEvent', referenceToFunction, phase)

Hierbei ist die .eventPhase
0 für ein selbst erzeugtes Element, das noch nicht gefeuert hat
1 während der capture phase
2 während der bubble phase
3 während der bubble phase beim target-Element, das das Ereignis auslöste (ancestor)

Zu einem load events gehört lediglich eine capture phase

 el.onclick = function () { alert('Element='+el.tagName() ); };
Propagation (Capture- und Bubble-Phase) Wie können 2 Elemente das gleiche Ereignis mithören?

Wie werder Ereignisse weitergereicht?

Der folgende Code hält sich (überwiegend) an W3C. Dieser Code ist nicht voll funktionsfähig und dient lediglich zur Veranschaulichung der Capture- und Bubble-Phasen. In einem div-Element ist ein span-Element und in diesem ein a-Element, etwa

<div id="MYDIV">
 <span id="MYSPAN">
   <a href="/" id="MYA">Bitte hier Klicken</a>
</span>
</div>
 if( document.implementation.hasFeature('MutationEvents','2.0') || window.MutationEvent ){...

 function init_capture_bubble_test(todo) {
    var div = document.getElementById('MYDIV'), 
    span = document.getElementById('MYSPAN'),
    a = document.getElementById('MYA');

    div.addEventListener('click', function (ev) { 
                alert('↓ div-Element capture phase '+info(ev)); }, true);
    span.addEventListener('click', function (ev) { 
                 alert('↓ span-Element capture phase '+info(ev)); }, true);
    a.addEventListener('click', function (ev) { 
              alert('kein Standard: zusätzliche a-Element capture phase '+info(ev)); }, true);

    a.addEventListener('click', function (ev) { 
              alert('↑ a-Element bubble phase (1. listener) '+info(ev)); }, false);

    // a.addEventListener('click', function (ev) { 
    //           alert('↑ a-Element bubble phase (2. listener) '+info(ev)); }, false);
    // anstelle "return false": if(ev.cancellable){ev.preventDefault();}

    span.addEventListener('click', function (ev) { alert('↑ span-Element bubble phase (3. listener) '+info(ev)); }, false);

    if (todo === 'fired_once') {
      a.addEventListener('click', function (ev) {
         alert('a-Element bubble (3. listener) fired once (this.removeEventListener(\'click\', arguments.callee, false) '+info(ev));
         this.removeEventListener('click', arguments.callee, false);
         // alternativ ev.target.removeEventListener(...)
      }, false);
    }
    div.addEventListener('click', function (ev) { 
       alert('↑ div-Element phase (4. listener), keine weiteren Listener: ev.stopPropagation();'+ev);
       //ev.stopPropagation();
    }, false);
    document.stopPropagation();
 }
 
 function get_target(ev) { var o, mr, d = document.documentElement, b = document.body,
   ev = ev || window.event, t = ev.target || ev.srcElement, k = ev.keyCode || ev.which;
   // Maus-Taste, Maus-Position
   if (ev.button) { mr = 2 === e.button; } else if (ev.which) { mr = 3 === e.which; }
   if ( ev.pageX == null && ev.clientX != null ) {
        ev.pageX = e.clientX + (d && d.scrollLeft || b && b.scrollLeft || 0) 
                 - (d && d.clientLeft || b && b.clientLeft || 0);
        ev.pageY = ev.clientY + (d && d.scrollTop  || b && b.scrollTop  || 0) 
                 - (d && d.clientTop  || b && b.clientTop  || 0);
   }
   if (t && (ev.target.nodeType === 3 || ev.target.nodeType === 4)) { t = t.parentNode; }
   o = { ev: ev, el: t, keycode: k, char: String.fromCharCode(k), 
         mouse_r: mr, x: ev.pageX, y: ev.pageY};
   return o;
}

function info(ev) { var ev = ev || window.event, s = 
   "\n ev.currentTarget.nodeName="+ev.currentTarget.nodeName +
   "\n ev.currentTarget.nodeType="+ev.currentTarget.nodeType +
   "\n ev.target.nodeName="+ev.target.nodeName + 
   "\n ev.target.nodeType="+ev.target.nodeType+
   "\n MS ev.srcElement.nodeName=" + ev.srcElement.nodeName +
   "\n MS ev.srcElement.nodeType=" + ev.srcElement.nodeType;
   "\n ev.cancelable="+ev.cancelable+
   "\n document.implementation.hasFeature(\'MutationEvents\',\'2.0\') || window.MutationEvent="+document.implementation.hasFeature('MutationEvents','2.0') || window.MutationEvent;
   return s;
}
Event-Objekt Welche Properties enthält das Event?

Nach W3C wird das Event-Objekt an den Handler weiter gegeben. Hinweis: preventDefault() oder stopPropagation() wirken sich auf die aktuelle Event-Instanz aus. Der IE verwendet das global window.event-Objekt.

Die folgenden Tabellen basieren auf Zusammenstellungen von Mike Hall (siehe http://brainjar.com/dhtml/events/default4.asp). Es werden nicht alle Properties angegeben. Es fehlen z.B. Mauskoordinaten, Tastencode, usw.

W3C Event model
Event Properties and Methods
bubbles A Boolean value indicating whether or not the event bubbles.
cancelable A Boolean value indicating whether or not the event can be canceled.
currentTarget The node that this event handler is assigned to.
eventPhase An integer value indicating which phase of the event flow this event is being processed in. One of CAPTURING_PHASE (1), AT_TARGET (2) or BUBBLING_PHASE (3).
target The node that the event originated from.
timeStamp The time the event occurred.
type A string indicating the type of event, such as "mouseover", "click", etc.
W3C
event
IE
window.event
Notes
currentTarget none
target srcElement The node that the event originated from. See below.
eventPhase none Not applicable in IE.
timeStamp none Not available in IE.
type type Equivalent to the standard.
preventDefault() returnValue Set to false to cancel any default action for the event.
stopPropagation() cancelBubble Set to true to prevent the event from bubbling.

Beispiel: 'mouseover', 'mouseup' Ereignis-Properties per Proramm
Code Snippet: 'mouseover', 'mouseup' in der bubble-phase
  1. var el = document.getElementById('id_fuer_do_ev');
  2. if( el.addEventListener ) {
  3.     el.addEventListener('mouseover', do_ev1, false);
  4.     el.addEventListener('mouseup',   do_ev2, false);
  5. } else if( el.attachEvent ) {
  6.     el.attachEvent('onmouseover', do_ev1);
  7.     el.attachEvent('onmouseup',   do_ev2);
  8. }

Die Funktionen do_ev1(ev) bzw. do_ev2(ev), die bei einem 'mouseover'- bzw. 'mouseup'-Ereignis ausgeführt werden, sehen etwa so aus:

Code Snippet: Event-Behandlung (.createEvent)
  1. function do_ev1(ev) {
  2.     var o, t = ev.target ? ev.target : ev.srcElement;
  3.     if( t && ( t.nodeType == 3 ) ) { t = t.parentNode; }
  4.  
  5.     if( document.createEvent ) { o = document.createEvent('MouseEvents');
  6.       o.initMouseEvent('mouseup',true,false,window,
  7.                         0,12,345,7,220,false,false,true,false,0,null);
  8.       t.dispatchEvent(o);
  9.     } else if( document.createEventObject ) { o = document.createEventObject();
  10.       o.detail = 0;
  11.       o.screenX = 12;    o.screenY = 345;
  12.       o.clientX = 7;     o.clientY = 220;
  13.       o.ctrlKey = false; o.altKey = false;
  14.     o.shiftKey = true; o.metaKey = false;
  15.       o.button = 0;      o.relatedTarget = null;
  16.       t.fireEvent('onmouseup',o);
  17.     }
  18. }
  19.  
  20. function do_ev2(ev) { var s = '', prop,
  21.   props=',type,target,srcElement,currentTarget,bubbles,cancelable,windowObject,detail,'+
  22.         'screenX,screenY,clientX,clientY,ctrlKey,altKey,shiftKey,metaKey,button,relatedTarget,';
  23.   for(prop in ev) {
  24.     if(props.indexOf(',' + prop + ',') + 1) { s += '\n' + prop + ': '+
  25.       ( (typeof(ev[prop]) === 'object')?'Element' : ev[prop] );
  26.     }
  27.   } alert('Event fired:\n'+s);
  28. }

Teste: 'mouseover', 'mouseup'

Beispiel (Hintergrundfarbe, einführend) Setze zeitgesteuert Grün

Die Funktion fade_simple = function(node, ms) soll den Grünanteil 'gg'der Hintergrundfarbe zeitgesteuert (Closure) setzen: gg = '00', '11', '22', '33', '44', '55', '66', '77', '88', '99', 'aa', 'bb', 'cc', 'dd', 'ee', 'ff'.

Code Snippet: Hintergrundfarbe (einführend)
  1. var fade_simple = function(node, ms) {
  2.    var i=0, gg, step; ms = ms || 100;
  3.  
  4.    step = function() {
  5.       gg = i.toString(16) + i.toString(16);
  6.       node.style.backgroundColor = "#ff" + gg + "ff";
  7.        if(i < 15) {
  8.         i += 1;
  9.         setTimeout(step,ms);
  10.       }
  11.    }; setTimeout(step, ms);
  12. };
  13.  
  14. // window.onload = function() { fade_simple(document.body); }

Hier ist die Ausführung der Funktion fade_simple(document.getElementById('FADE_SIMPLE')): Test

Beispiel (Hintergrundfarbe, Farbverlauf) Setze zeitgesteuert Hintergrundfarben

Dies ist eine Erweiterung des vorherigen Beispieles, wobei die inneren Hilfsfunktionen r_g_b_from(col) und function hex_from(dez) verwendet werden.

Code Snippet: Hintergrundfarbe (Farbverlauf)
  1. var fade = function(node, col1, col2, anz, ms)
  2. {
  3.   var i=0, o1,o2, r,g,b, dr,dg,db, h, col, step;
  4.   
  5.   function r_g_b_from(col) { // z.B. col="#ffff8a";
  6.     if(col.length !== 7){ return {r:255,g:255,b:0}; }
  7.     r = parseInt(col.substr(1,2),16);
  8.     g = parseInt(col.substr(3,2),16);
  9.     b = parseInt(col.substr(5,2),16); return {'r':r,'g':g,'b':b};
  10.   }
  11.  
  12.   function hex_from(dez) {
  13.     dez = Math.min(255,dez); h = Math.round(dez).toString(16);
  14.     if(h.length < 2) {return "0" + h;} return h;
  15.   }
  16.   // default
  17.   col1 = col1 || '#660000'; anz = anz || 80;
  18.   col2 = col2 || '#ffffff'; ms = ms || 50;
  19.   
  20.   o1 = r_g_b_from(col1); o2 = r_g_b_from(col2);
  21.   dr = (o2.r - o1.r) / anz;
  22.   dg = (o2.g - o1.g) / anz;
  23.   db = (o2.b - o1.b) / anz;
  24.   
  25.   step = function() {
  26.     col="#" + hex_from(o1.r) + hex_from(o1.g) + hex_from(o1.b);
  27.     node.style.backgroundColor = col;
  28.     o1.r += dr; o1.g += dg; o1.b += db;
  29.     if(i < anz) { i += 1; setTimeout(step,ms); }
  30.   }; setTimeout(step, ms);
  31. };
  32.  
  33. //fade(document.body);

Hier ist die Ausführung der Funktion fade(document.body): Test

Beispiel (onclick-Handler, Closure) Was bedeutet Closure?

Mit der folgenden Funktion add_handlers_to sollen DOM-Elementen eines Array's jeweils einige CSS-Attribute und ein onclick-Ereignis hinzu gefügt werden.

Wichtig ist, dass die Handlerfunktion nicht an die Variable i, sondern an den Wert von i beim Click-Ereignis gebunden wird.

Lösung: Es wird eine Funktion = function (i) {...}(i) definiert, die sofort mit i aufgerufen wird. Dadurch wird eine Eventhandler-Funktion zurück gegeben, die an den übergebenen Wert von i gebunden ist (und nicht an add_handlers_to). Diese zurück gegebene Funktion wird dann onclick zugewiesen.

Code Snippet: onclick-Handler (Closure)
  1. var add_handlers_to = function (nodes) { var i;
  2.   for(i = 0; i < nodes.length; i += 1) {
  3.     nodes[i].style.cursor  = 'pointer';
  4.     nodes[i].style.padding = '3px';
  5.     nodes[i].style.border  = '3px outset #ccc';
  6.     
  7.     nodes[i].onclick = function (i) {
  8.       return function (e) {
  9.         var d = document.documentElement, b = document.body;
  10.         if (!e) { e = window.event;} //IE
  11.         // Maus-Position
  12.         if ( e.pageX == null && e.clientX != null ) {
  13.          e.pageX = e.clientX + (d && d.scrollLeft || b && b.scrollLeft || 0)
  14.                              - (d && d.clientLeft || b && b.clientLeft || 0);
  15.          e.pageY = e.clientY + (d && d.scrollTop  || b && b.scrollTop  || 0)
  16.                              - (d && d.clientTop  || b && b.clientTop  || 0);
  17.         }
  18.         alert(e.type+' auf id='+this.id+' mit (x,y)=('+e.pageX+','+e.pageY+')');
  19.       };  // ende  return function (e)
  20.     }(i); // ende  nodes[i].onclick
  21.   }
  22. };
  23. add_handlers_to([
  24. document.getElementById('id0'),
  25. document.getElementById('id1'),
  26. document.getElementById('id2')
  27. ]);

Zum Testen bitte auf die id's klicken. id0  id1  id2