XBL – XML Binding Language /3

Siamo giunti all’ultimo appuntamento con XBL dopo aver visto come modificare l’aspetto grafico dei componenti e come eseguire operazioni
tramite metodi Javascript.
In questo articolo ci concentreremo sull’utilizzo dei gestori di eventi e faremo la conoscenza dell’Anonymous Content.

Gestori di evento

Sappiamo che i componenti XBL sono una sorta di aggregazione di widget esistenti dove ognuno singolarmente continua a ricevere le notifiche sui propri eventi.
Due widget che dichiarano entrambi il gestore per onclick riceveranno entrambi la notifica.
Nell’esempio del filepicker l’apertura della finestra di selezione file viene gestita dall’evento oncommand dichiarato
sul pulsante e non sul nostro componente XBL, che ricordiamo è composto da un campo di testo più il citato pulsante.

<binding id="filepickerbinding">

<content>
    <xul:hbox>
        <xul:textbox />
        <xul:button oncommand="onPickFile();" />
    </xul:hbox>
</content>

</binding>

Per far si che il componente XBL abbia dei propri gestori di eventi è necessario scrivere degli handlers come nodi figli di binding. Qui di seguito sono riportati vari esempi.

<binding id="...">
  <handlers>
    <handler event="command"><![CDATA[
      // codice di gestione
      ]]>
    </handler>
  
    <!— Quando il codice è inline può essere
            utilizzato l'attributo action —>
    <handler event="keypress"
                keycode="VK_ENTER"
                action="this.OnEnter()" />
  </handlers>
</binding>

Ogni handler riceve un parametro implicito di nome event: il tipo è lo stesso dell’oggetto event ricevuto in ingresso negli handler DOM (onclick, onchange). Si faccia riferimento alle specifiche W3C Event per maggiori dettagli.

Eventi mouse e key

Gli eventi legati al mouse e alla pressione di tasti possono avere attributi che filtrano con maggiore accuratezza: ad esempio, è possibile intercettare la pressione del tasto Enter specificando l’attributo keycode.

<handler event="keypress" keycode="VK_ENTER" action="this.OnEnter()" />

Oppure gestire manualmente il tasto premuto

<handler event="keypress"><![CDATA[
  if (event.keyCode == VK_RETURN) {
      ...
  } else if (event.keyCode == VK_UP) {
      ...
  }
  ]]>
</handler>

La lista completa delle costanti dei nomi dei tasti è presente in keyref.

La stessa cosa può essere fatta per intercettare quale tasto del mouse viene premuto utilizzando l’attributo button

  <handler event="click" button="0"><![CDATA[
    // click del mouse con il tasto sinistro
    ]]>
  </handler>

  <handler event="mousedown" button="1"><![CDATA[
    // il tasto centrale è premuto
   ]]>
  </handler>

  <handler event="click"><![CDATA[
    if (event.button == 0) {
        // sinistro
    } else if (event.button == 1) {
        // centrale
    } else if (event.button == 2) {
        // destro
    }
    ]]>
  </handler>
Tabella 1. Valori ammessi per button
Valore Significato
0 Tasto Sinistro
1 Tasto Centrale
2 Tasto Destro

E’ anche possibile controllare se è stato premuto un tasto speciale come Shift o Control tramite l’attributo modifier.

<handler event="keypress"
            keycode="VK_UP"
            modifiers="meta"
            action="this.moveUp()"/>
Tabella 2. Valori ammessi per modifier
Valore Significato
alt Tasto Alt
control Tasto Control
meta Tasto Meta valido solo su Mac
shift Tasto Shift
accel Tasto speciale specifico della piattaforma (ad esempio, su Windows tasto Control, su Mac tasto Command)

Come al solito e’ possibile scegliere di controllare da codice il valore associato all’evento

<handler event="keypress" keycode="VK_UP"><![CDATA[
    if (event.altKey) {
    } else if (event.shiftKey) {
    } else if (event.metaKey) {
    }
]]>
</handler>
Tabella 3. Valori ammessi nell’oggetto event corrispondenti ai modifier
Valore Significato
altKey Tasto Alt
ctrlKey Tasto Control
metaKey Tasto Meta valido solo su Mac
shiftKey Tasto Shift

Le fasi di un evento

Il modello DOM prevede che un evento raggiunga l’elemento che lo ha generato secondo una serie di fasi
ben descritte in Events flow e
Events order.

Le fasi sono:

  • capturing phase
  • target phase
  • bubbling phase

Le fasi stabiliscono in che ordine un evento deve raggiungere gli elementi interessati.
Uno scenario frequente vede un nodo padre e un nodo figlio che hanno entrambi un handler per lo stesso evento.

Se si vuole processare l’evento solo in uno dei nodi oppure decidere chi deve essere richiamato prima bisogna
utilizzare l’attributo phase che può assumere i valori riportati in Tabella 4.

Tabella 4. Valori ammessi per l’attributo phase
Valore
capturing
target
bubbling (il valore di default se non specificato)

Durante la fase capturing l’evento raggiunge l’elemento che lo ha scatenato solo dopo che è stato passato
alla window, al document di appartenenza e a tutti gli elementi parent.

La fase bubbling va nel verso opposto di capturing, l’evento risale dal target (elemento che ha originato l’evento)
verso il document e poi verso la window di appartenenza.

Durante queste fasi è possibile interrompere e/o modificare il cammino dell’evento ad esempio con
event.stopPropagation().

Durante la fase target l’handler viene
chiamato solo se event.target è lo stesso dell’elemento che ha originato l’evento.
Ciò equivale a racchiudere il codice in:

if (event.target == this) {
    // questo elemento ha generato l'evento
}

Le fasi degli eventi in XBL non introducono nulla di nuovo rispetto al modello a eventi di Mozilla, semplicemente
è possibile intervenire ad un livello più basso.

Anonymous Content

Il nostro filepicker ha nel nodo content i widget che lo compongono, il campo di testo e il pulsante.
La loro presenza viene nascosta allo sviluppatore che interagisce con il nostro oggetto solo tramite
il tag <filepicker>.
I widget rappresentano un contenuto anonimo dato che dal nodo parent, ovvero dall’esterno, non è possibile accedervi.
Ovviamente l’implementazione può accedere ai vari elementi che costituiscono il componente XBL (se così non fosse non risulterebbe di nessuna utilità la presenza del contenuto anonimo).

Accedere al contenuto anonimo dall’implementazione

Per accedere a un elemento DOM sappiamo che è sufficiente utilizzare document.getElementById()
e ci si aspetterebbe che XBL avesse lo stesso comportamento ma purtroppo non è così, l’accesso ai singoli widget è
leggermente più complicato.

L’accesso agli elementi viene fatto tramite il metodo getAnonymousElementByAttribute(element, attr, value),
che recupera un nodo a partire da un parent element (primo argomento) e dalla coppia nome attributo e valore attributo.
Questa è una differenza importante rispetto al metodo document.getElementById() che implicitamente
cerca un attributo di nome id.

Dato che ottenere un elemento DOM a fronte del proprio id è una operazione assai utile e frequente in
XBL si utilizza la convenzione di assegnare agli elementi l’attributo anonid e recuparne il nodo
tramite getAnonymousElementByAttribute().
Ad esempio per ottenere un riferimento al pulsante del filepicker si scrive

<field name="mButton">
document.getAnonymousElementByAttribute(
            this,
            "anonid",
            "pickbutton")
</field>

Dove il binding è così dichiarato (notare gli anonid)

<content>
    <xul:hbox xbl:inherits="flex">
        <xul:textbox anonid="textbox" />
        <xul:button anonid="pickbutton" />
    </xul:hbox>
</content>

Si noti che il nome anonid è del tutto arbitrario e convenzionale, si sarebbe potuto usare un proprio nome (come ad esempio lo stesso id), ma per evidenziare il fatto che stiamo lavorando con contenuti anonimi anonid risulta più appropriato.

Una volta ottenuto l’accesso al nodo lo si usa allo stesso modo di un normale elemento DOM con
getAttribute()/setAttribute()/removeAttribute()

var button = document.getAnonymousElementByAttribute(this,
                    "anonid", "pickbutton");
// Imposta il contenuto della casella di testo
button.setAttribute("value", "/tmp");

Conclusioni

All’interno della piattaforma Mozilla XBL è un tassello importante e come si è visto abbastanza flessibile.
Si è cercato di fare una panoramica dei costrutti più utili realizzando un esempio semplice ma con
una utilità pratica reale.
L’oggetto filepicker che abbiamo visto infatti è un buon punto di partenza per fare qualche sperimentazione ed è
riutilizzabile in altri progetti già così com’è (ad esempio, ViewSourceWith ne fa uso).

Policy per i commenti: Apprezzo moltissimo i vostri commenti, critiche incluse. Per evitare spam e troll, e far rimanere il discorso civile, i commenti sono moderati e prontamente approvati poco dopo il loro invio.