L'Observer pattern è un design pattern che, come testimonia il suo nome, tiene traccia dei cambiamenti di stato di altri oggetti, li registra ed esegue azioni all'occorrenza. In jQuery questo design pattern si rivela estremamente utile nel caso specifico delle animazioni. Inoltre questo design pattern può essere combinato con il metodo data()
per ottenere una maggiore integrazione con gli oggetti di jQuery. Vediamone una semplice implementazione.
Il nostro esempio
Abbiamo un box all'interno di un contenitore posizionato in modo contestuale. Cliccando su un link vogliamo che il box si sposti verso destra di 100 pixel, modifichi la sua altezza, torni alla posizione iniziale e riacquisti la sua altezza originale.
Il nostro CSS è il seguente:
#container {
width: 500px;
height: 100px;
position: relative;
border: 1px solid #000;
}
#animated {
width: 100px;
height: 100px;
background: #0084e4;
position: absolute;
top: 0;
left: 0;
}
L'Observer pattern
Creiamo innanzitutto un singleton che chiameremo Observer
:
var Observer = new function() {
//...
}();
Aggiungiamo una proprietà per memorizzare la posizione corrente dell'elemento da animare:
var Observer = new function() {
this.position = 0;
// continua
}();
Il valore iniziale è ovviamente 0. Ora ci occorrono un getter ed un setter per leggere ed impostare il valore di tale posizione:
// continua
this.setPosition = function(pos) {
this.position = pos;
};
this.getPosition = function() {
return this.position;
};
A questo punto possiamo creare un metodo per aggiungere un'azione di callback quando ci occorre:
this.addAction = function(action) {
if(typeof action === 'function') {
action();
}
return this;
};
return this
ci consente di utilizzare uno stile a cascata nel nostro codice, così da avere Observer.metodo1().metodo2().metodoN()
. Ora possiamo creare il metodo principale di questo oggetto che chiameremo observe()
:
this.observe = function(element) {
element = element || this;
console.log('Selected element: ' + element[0].tagName);
this.setPosition(element.position().left);
console.log(this.getPosition());
return this;
};
Questo metodo invia subito alla console JavaScript il nome del tag dell'elemento selezionato. Quindi imposta la proprietà position
usando a sua volta la proprietà left
dell'omonimo oggetto di jQuery. Infine, invia tale posizione alla console JavaScript.
Quindi abbiamo un oggetto che:
- tiene traccia della posizione dell'elemento
- esegue un'azione su ogni cambiamento di posizione
Usare data()
data()
permette di associare ad un elemento un oggetto e di accedere ai metodi e alle proprietà di tale oggetto tramite la seguente sintassi:
$(elemento).data('riferimento', oggetto);
oggetto
è ora associato all'elemento jQuery e possiamo accedervi tramite il riferimento specificato come primo parametro:
$(elemento).data('riferimento').metodo();
$(elemento).data('riferimento').proprieta;
Questo metodo è molto utile ai fini della performance: associando un'oggetto ad un elemento jQuery rendiamo i suoi metodi e proprietà immediatamente accessibili.
Da notare, tuttavia, che un riferimento all'elemento corrente tramite this
o $(this)
all'interno dell'oggetto associato non è possibile, perchè ovviamente tali parole chiave faranno riferimento all'oggetto e non all'elemento jQuery.
L'esempio finale
Il codice finale è il seguente:
$(function() {
function setHeight(element, speed, height) {
$(element).animate({
height: height
}, speed);
}
$('#animated').data('observer', Observer);
var animated = $('#animated');
$('#run').click(function(e) {
$('#animated').data('observer').observe(animated);
$('#animated').animate({
left: 100
}, 'slow', function() {
$(this).data('observer').observe(animated).
addAction(setHeight(animated, 'slow', 50));
$(this).animate({
left: 0
}, 'slow', function() {
$(this).data('observer').observe(animated).
addAction(setHeight(animated, 'slow', 100));
});
});
e.preventDefault();
});
});
Per prima cosa associamo Observer
al nostro elemento:
$('#animated').data('observer', Observer);
Memorizziamo quindi l'elemento nella variabile animated:
var animated = $('#animated');
Usiamo Observer::observe()
all'inizio dell'animazione:
$('#animated').data('observer').observe(animated);
Lo usiamo di nuovo dopo il primo spostamento verso destra e gli associamo l'azione definita nella funzione setHeight()
:
$(this).data('observer').observe(animated).
addAction(setHeight(animated, 'slow', 50));
Ora l'elemento tornerà di nuovo nella posizione di partenza. Usiamo di nuovo il nostro oggetto e riportiamo l'altezza dell'elemento alla dimensione originaria:
$(this).data('observer').observe(animated).
addAction(setHeight(animated, 'slow', 100));
Aprendo la console JavaScript del vostro browser ad animazione terminata potrete osservare i messaggi di stato generati dal nostro oggetto.
Potete visionare l'esempio finale in questa pagina. Per chi fosse interessato a questo design pattern, segnalo questo plugin.