Eventi personalizzati
Indice
Gli eventi personalizzati consentono di farsi un’idea del mondo della programmazione orientata a oggetti (in inglese event-driven programming). In questo capitolo useremo il sistema di eventi personalizzati di jQuery per creare una semplice applicazione di ricerca Twitter.
In un primo momento può essere difficile da capire il requisito di utilizzare eventi personalizzati, visto che gli eventi convenzionali permettono di soddisfare tutte le esigenze. Tuttavia, gli eventi personalizzati offrono un modo nuovo di pensare la programmazione in JavaScript. Invece di concentrarsi sull’elemento che esegue un’azione, gli eventi personalizzati pongono l’attenzione sull’elemento in cui si verifica l’azione. Questo concetto offre diversi vantaggi:
- Il comportamento dell’elemento obiettivo può essere eseguito da diversi elementi utilizzando lo stesso codice.
- I comportamenti possono essere eseguiti in più o simili elementi obiettivi alla volta.
- I comportamenti sono associati più chiaramente con l’elemento di obiettivo, rendendo il codice più facile da leggere e manutenere.
Un esempio è il modo migliore per spiegare la questione. Supponiamo di avere una lampada a incandescenza in una stanza di una casa. La lampada è attualmente accessa. La stessa è controllata da due interruttori a tre posizioni e un clapper (switch attivato per applausi):
<div class="lightbulb on"></div>
<div class="switch"></div>
<div class="switch"></div>
<div class="clapper"></div>
</div>
[/code]
Senza l’utilizzo di eventi personalizzati, è possibile scrivere la routine come segue:
var $light = $(this).parent().find(‘.lightbulb’);
if ($light.hasClass(‘on’)) {
$light.removeClass(‘on’).addClass(‘off’);
} else {
$light.removeClass(‘off’).addClass(‘on’);
}
});
[/code]
var $light = $(this);
if ($light.hasClass(‘on’)) {
$light.removeClass(‘on’).addClass(‘off’);
} else {
$light.removeClass(‘off’).addClass(‘on’);
}
});
$(‘.switch, .clapper’).click(function() {
$(this).parent().find(‘.lightbulb’).trigger(‘changeState’);
});
[/code]
E ‘anche possibile fare un esempio po’ più interessante. Supponiamo di aggiungere un’altra stanza nella casa, insieme ad uno switch generale, come illustrato di seguito:
<div class="lightbulb on"></div>
<div class="switch"></div>
<div class="switch"></div>
<div class="clapper"></div>
</div>
<div class="room" id="bedroom">
<div class="lightbulb on"></div>
<div class="switch"></div>
<div class="switch"></div>
<div class="clapper"></div>
</div>
<div id="master_switch"></div>
[/code]
.bind(‘changeState’, function(e) {
var $light = $(this);
if ($light.hasClass(‘on’)) {
$light.trigger(‘turnOff’);
} else {
$light.trigger(‘turnOn’);
}
})
.bind(‘turnOn’, function(e) {
$(this).removeClass(‘off’).addClass(‘on’);
})
.bind(‘turnOff’, function(e) {
$(this).removeClass(‘off’).addClass(‘on’);
});
$(‘.switch, .clapper’).click(function() {
$(this).parent().find(‘.lightbulb’).trigger(‘changeState’);
});
$(‘#master_switch’).click(function() {
if ($(‘.lightbulb.on’).length) {
$(‘.lightbulb’).trigger(‘turnOff’);
} else {
$(‘.lightbulb’).trigger(‘turnOn’);
}
});
[/code]
- Il metodo $.fn.bind prende come argomenti un tipo di evento e una funzione di gestione degli eventi. Opzionalmente, può ricevere informazioni associate all’evento come secondo argomento, spostando come terzo argomento la funzione gestore degli eventi. Qualunque informazione passata sarà disponibile alla funzione attraverso della proprietà data dell’oggetto dell’evento. A sua volta, la funzione del gestore degli eventi riceve l’oggetto dell’evento come primo argomento.
- Il metodo $.fn.trigger prende come argomenti il tipo di evento e facoltativamente può prendere un array di valori. Questi valori saranno passati alla funzione del gestore degli eventi come argomentidopo dell’oggetto dell’evento.
Di seguito è riportato un esempio d’uso di $.fn.bind e $.fn.trigger in cui si utilizza le informazioni personali in entrambi i casi:
console.log(e.data.foo); // ‘bar’
console.log(arg1); // ‘bim’
console.log(arg2); // ‘baz’
});
$(document).trigger(‘myCustomEvent’, [ ‘bim’, ‘baz’ ]);
[/code]
Per dimostrare la potenza di eventi personalizzati, svilupperà un semplice strumento per la ricerca di Twitter. Questo strumento offrirà diversi modi per effettuare una ricerca dall’utente: inserirendo il termine da ricercare in una casella di testo o consultando i “temi caldi” di Twitter.
I risultati di ciascun termine verranno visualizzati in un contenitore di risultati; questi risultati si potranno espandere, comprimere, aggiornare e rimuovere, singolarmente o l’insieme.
l risultato finale dell’applicazione sarà la seguente:
Iniziazione
<input type="button" id="get_trends"
value="Load Trending Terms" />
<form>
<input type="text" class="input_text"
id="search_term" />
<input type="submit" class="input_submit"
value="Add Search Term" />
</form>
<div id="twitter">
<div class="template results">
<h2>Search Results for
<span class="search_term"></span></h2>
</div>
</div>
[/code]
Ci sono due tipi di elementi su cui agire: il contenitore di risultati e il contenitore Twitter.
I contenitori di risultati è cuore dell’applicazione. Si creerà un’estensione per preparare ogni contenitore una volta che questo viene aggiunto al contenitore Twitter. Inoltre, tra le altre cose, l’estensione vincolerà gli eventi personalizzati per ogni contenitore e aggiungerà, in alto a destra di ogni contenitore, pulsanti che eseguiranno azioni. Ogni contenitore di risultati dovrà avere i seguenti eventi personalizzati:
refresh
populate
remove
collapse
expand
return this.each(function() {
var $results = $(this),
$actions = $.fn.twitterResult.actions =
$.fn.twitterResult.actions ||
$.fn.twitterResult.createActions(),
$a = $actions.clone().prependTo($results),
term = settings.term;
$results.find(‘span.search_term’).text(term);
$.each(
[‘refresh’, ‘populate’, ‘remove’, ‘collapse’, ‘expand’],
function(i, ev) {
$results.bind(
ev,
{ term : term },
$.fn.twitterResult.events[ev]
);
}
);
// utilizza la classe di ogni azione per
// determinare quale evento si svolgerà
// nel riquadro dei risultati
$a.find(‘li’).click(function() {
// passa l’elemento <li> cliccato
// sulla funzione in modo da poterlo
// manipolare, se necessario
$results.trigger($(this).attr(‘class’), [ $(this) ]);
});
});
};
$.fn.twitterResult.createActions = function() {
return $(‘<ul class="actions" />’).append(
‘<li class="refresh">Refresh</li>’ +
‘<li class="remove">Remove</li>’ +
‘<li class="collapse">Collapse</li>’
);
};
$.fn.twitterResult.events = {
refresh : function(e) {
// indica che i risultati sono
// in fase di aggiornamento
var $this = $(this).addClass(‘refreshing’);
$this.find(‘p.tweet’).remove();
$results.append(‘<p class="loading">Loading …</p>’);
// ottiene le informazioni da Twitter
// in formato jsonp
$.getJSON(
‘http://search.twitter.com/search.json?q=’ +
escape(e.data.term) + ‘&rpp=5&callback=?’,
function(json) {
$this.trigger(‘populate’, [ json ]);
}
);
},
populate : function(e, json) {
var results = json.results;
var $this = $(this);
$this.find(‘p.loading’).remove();
$.each(results, function(i,result) {
var tweet = ‘<p class="tweet">’ +
‘<a href="http://twitter.com/’ +
result.from_user +
‘">’ +
result.from_user +
‘</a>: ‘ +
result.text +
‘ <span class="date">’ +
result.created_at +
‘</span>’ +
‘</p>’;
$this.append(tweet);
});
// indica che i risultati
// sono già stati aggiornati
$this.removeClass(‘refreshing’);
},
remove : function(e, force) {
if (
!force &&
!confirm(‘Rimuovere pannello per il termine ‘ + e.data.term + ‘?’)
) {
return;
}
$(this).remove();
// indica che si non avrà
// un pannello per il termine
search_terms[e.data.term] = 0;
},
collapse : function(e) {
$(this).find(‘li.collapse’).removeClass(‘collapse’)
.addClass(‘expand’).text(‘Expand’);
$(this).addClass(‘collapsed’);
},
expand : function(e) {
$(this).find(‘li.expand’).removeClass(‘expand’)
.addClass(‘collapse’).text(‘Collapse’);
$(this).removeClass(‘collapsed’);
}
};
[/code]
.bind(‘getResults’, function(e, term) {
// si verifica che non ci sia
// un contenitore per il termine
if (!search_terms[term]) {
var $this = $(this);
var $template = $this.find(‘div.template’);
// fa una copia del modello div e la inserisce
// come il primo contenitore di risultati
$results = $template.clone().
removeClass(‘template’).
insertBefore($this.find(‘div:first’)).
twitterResult({
‘term’ : term
});
// carica il contenuto utilizzando
// l’evento personalizzato "refresh"
// vincolato al contenitore dei risultati
$results.trigger(‘refresh’);
search_terms[term] = 1;
}
})
.bind(‘getTrends’, function(e) {
var $this = $(this);
$.getJSON(‘http://search.twitter.com/trends.json?callback=?’, function(json) {
var trends = json.trends;
$.each(trends, function(i, trend) {
$this.trigger(‘getResults’, [ trend.name ]);
});
});
});
[/code]
Di seguito, si collega la casella di ricerca e pulsante per caricare i “temi caldi”. Nella casella, il termine digitato viene catturato e passato, allo stesso tempo che si esegue getResults. D’altra parte, cliccando sul pulsante per caricare i “temi caldi”, si esegue l’evento getTrends:
e.preventDefault();
var term = $(‘#search_term’).val();
$(‘#twitter’).trigger(‘getResults’, [ term ]);
});
$(‘#get_trends’).click(function() {
$(‘#twitter’).trigger(‘getTrends’);
});
[/code]
$(‘#’ + ev).click(function(e) { $(‘#twitter div.results’).trigger(ev); });
});
$(‘#remove’).click(function(e) {
if (confirm(‘Remove all results?’)) {
$(‘#twitter div.results’).trigger(‘remove’, [ true ]);
}
});
[/code]
Una volta che i comportamenti sono stati descritti, diventa banale da eseguire da qualunque luogo, consentendo la creazione rapida e la sperimentazione di opzioni di interfaccia. Infine, eventi personalizzati sono in grado di migliorare la leggibilità del codice e la sua manutenzione, rendendo chiara la relazione tra un elemento e il suo comportamento.
È possibile visualizzare l’applicazione completa dei files demos/custom-events/custom-events.html e demos/custom-events/js/custom-events.js che compongono questo libro.
http://github.com/rmurphey/jqfundamentals
Con il contributo di James Padolsey, Paolo Irish e gli altri. Per una storia completa dei contributi visitare il repository GitHub.
Copyright © 2011
Ancora nessun commento