Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

News scroller con Prototype

Impariamo a usare le classi di Prototype costruendo un semplice news scroller
Impariamo a usare le classi di Prototype costruendo un semplice news scroller
Link copiato negli appunti

Questo articolo è la continuazione di Tooltip con le classi di Prototype: la lettura del precedente articolo è consigliata, in quanto alcune nozioni di base sulla creazione delle classi Javascript sono date per assunte. Approfondiremo lo scripting object oriented consentito dall'utilizzo del framework Prototype attraverso la crezione di un esempio pratico: un News Scroller (verticale e orizzontale) non intrusivo, imitando il comportamento ottenuto con il tag <marquee>, un elemento deprecato che non è mai entrato negli standard del W3C. Interessantee al riguardo anche l'articolo "News scroller in Javascript non intrusivo" di Cesare Lamanna, il cui esempio (disponibile solo in versione verticale) non richiede alcuna libreria di supporto. Desidero menzionare anche il post originale sull'ereditarietà delle classi in Prototype, su Syntactic Sugar, che è senza dubbio un ottimo approfondimento sull'argomento della programmazione ad oggetti in Javascript.

Classes Inheritance con Prototype

Vediamo qualche esempio teorico su alcune delle funzionalità che utilizzeremo nel News Scroller. Con la versione di Prototype 1.6 si è aggiunta la possibilità di estendere le classi con facilità, da una classe principale a delle sottoclassi discendenti che ne ereditano i metodi, gli oggetti e le relative proprietà. Nel precedente articolo abbiamo conosciuto abbastanza approfonditamente la sintassi di Class.create() per la creazione di una classe e il suo metodo costruttore initialize(); diciamo ora che Class.create() accetta un numero arbitrario di argomenti. Se il primo è un'altra classe, la nuova classe eredita da essa e la estende. Tutti gli altri eventuali argomenti sono passati come metodi dell'istanza. Subito un po' di codice dove creiamo una classe base e una secondaria derivata da essa:

var baseClass = Class.create({
    initialize:function(){
		// Costruttore
	},

	somefunction : function(){
		$$('body')[0].insert("<p>Hai eseguito somefunction in una istanza di baseClass</p>");
	},

	anotherfunction : function(){
		$$('body')[0].insert("<p>Hai eseguito anotherfunction in una istanza di baseClass</p>");
	}
	
})

var BaseClassExtended = Class.create(baseClass, {
	initialize:function(){
	},

	somefunction : function(){		
		$$('body')[0].insert("<p>Hai eseguito somefunction in una istanza di BaseClassExtended</p>");
	},

	anotherfunction : function($super){
		$super();
		$$('body')[0].insert("<p>Hai eseguito anotherfunction in una istanza di BaseClassExtended</p>");
	}
})

Proviamo per prima cosa (esempio), il funzionamento della classe principale baseClass, istanziandola nella funzione startDemo() e invocando il metodo della classe somefunction():

	var startDemo = function() {
		var firstInstance = new baseClass();
		firstInstance.somefunction();
	}

	Event.observe (window, 'load', startDemo); 

Il risultato ottenuto non è niente di eccezionale, viene inserito nel documento un semplice paragrafo che ci conferma la nostra azione: "Hai eseguito somefunction in una istanza di baseClass". Continuiamo con un secondo esempio; stavolta prendiamo in esame la classe che estende la principale, BaseClassExtended, invocando il medesimo metodo somefunction(): per far questo bisogna modificare il contenuto della funzione starDemo() con una nuova istanza:

	var startDemo = function() {
		var secondInstance = new BaseClassExtended();
		secondInstance.somefunction();
	}

In questo caso, il paragrafo inserito dallo script ci notifica che la classe estesa ha sovrascritto il metodo somefunction() originario della classe base: "Hai eseguito somefunction in una istanza di BaseClassExtended".

Il prossimo passo puo essere allora tentare di eseguire entrambe le funzioni, quella estesa e quella della classe base, senza sovrascritture. Bisogna modificare la funzione starDemo() come sotto: viene creata un'altra istanza di BaseClassExtended e viene invocato il metodo comune delle due classi anotherfunction(): ecco il terzo ed ultimo esempio.

	var startDemo = function() {
		var thirdInstance = new BaseClassExtended();
		thirdInstance.anotherfunction();
	}

Semplicemente abbiamo ottenuto il risultato voluto (cioè l'esecuzione di entrambe le funzioni), vengono infatti inseriti due paragrafi: "Hai eseguito anotherfunction in una istanza di baseClass", "Hai eseguito anotherfunction in una istanza di BaseClassExtended". Tutto questo però è avvenuto solo grazie all'uso della funzione $super(), passata come argomento nel metodo della classe estesa (BaseClassExtended.anotherfunction): $super è a tutti gli effetti una parola riservata di Prototype.

Con gli esempi precedenti abbiamo già visto qualcosa, ma la loro semplicità non giustica il ricorso all'ereditarietà delle classi Javascript di Prototype. Un codice un po'più complesso, e la necessità di renderlo più versatile e flessibile, uniti a un esempio più concreto, possono rendere meglio l'idea degli effettivi benefici apportati da questi strumenti se utilizzati nei nostri script. L'occasione si presenta subito nel caso della realizzazione del News Scroller. Ho pensato infatti di strutturare lo script in modo da utilizzare tre classi. Una classe base, per eseguire le operazioni comuni e le altre due, estese dalla classe principale, ognuna con metodi specifici rispettivamente per lo scroll in orizzontale e per quello in verticale.

News Scroller: Markup HTML e regole CSS.

La struttura HTML utilizzata è molto semplice, e può essere la medesima per entrambi i tipi di scroll: è essenzialmente necessario inserire il contenuto che vogliamo scrollare in due <div>, il più esterno dei quali, di dimensioni definite, è il riquadro in cui visualizzare il contenuto in movimento. Vediamo subito un esempio anche se incompleto: da notare, a parte la mancanza di Javascript, che è necessario innanzitutto nascondere la parte di contenuto che eccede le dimensioni del div contenitore: assegnamo allora il valore hidden alla proprietà CSS overflow di questo elemento. Grazie a Javascript, potremo ciclicamente spostare a piccoli passi il div interno verso l'alto o verso sinistra, a seconda della direzione del movimento, e rendere visibile la parte nascosta. Se lo scroller da utilizzare è quello orizzontale, è necessario aggiungere qualche regola CSS per gli elementi del contenuto ( nel nostro caso dei paragrafi), che in pratica devono essere affiancati l'uno all'altro senza andare a capo. Basta modificarne le proprietà CSS float e display per renderli "inline".

/* scroller orizzontale */
#example {
 	border:1px solid #000;
 	;
 	;
 	overflow:hidden;
}

#example p {
 	padding:0 50px;
 	margin:0;
 	width:auto;
 	display:inline;
 	float:left;
 	white-space:nowrap;
	line-;
}

/* scroller verticale */
#example2 {
 	border:1px solid #000;
	background:#E1F0F9;
 	;
 	;
 	overflow:hidden;
}

#example2 div {
	padding:0 10px;
}

News Scroller: il codice Javascript.

Possiamo subito analizzare il codice della classe newsMarquee, che è la classe principale dello script del News Scroller. Le varie funzioni che la compongono saranno utilizzate dalle sue classi "figlie" estese (horizontalMarquee e verticalMarquee):

  • initialize(): è il metodo costruttore della classe che accetta due argomenti: l'elemento contenitore dello scroller di news e un hash di opzioni per il settaggio e la personalizzazione dello script (è possibile intervenire sulla velocità e sul controllo del movimento). Questa funzione si occupa tra le altre cose di distanziare opportunamente il div interno al contenitore per assicurare una corretta visualizzazione del contenuto in scorrimento, assegnandogli un padding laterale o verticale pari rispettivamente alla sua larghezza o altezza.
  • addObserver(): nel caso si sia deciso di consentire all'utente di controllare il movimento delle news, questa funzione intercetta gli eventi mouseover e mouseout del mouse sul riquadro dello scroller, e richiama di conseguenza i metodi playScroll() e pauseScroll().
  • playScroll(): di fatto "attiva" (o riattiva in seguito a un'eventuale pausa dello scroll) il movimento richiamando il metodo startScroll().
  • startScroll(): questa funzione utilizza il metodo di prototype PeriodicalExecuter ( l'equivalente di setInterval in semplice Javascript) che permette di eseguire ciclicamente una funzione. PeriodicalExecuter accetta due argomenti, la funzione da invocare (nel nostro caso executeScroll(), metodo delle classi figlie) e l'intervallo di esecuzione (in secondi).
  • pauseScroll(): questo metodo della classe ferma l'esecuzione in loop infinito della funzione executeScroll() richiamata da PeriodicalExecuter.
var newsMarquee = Class.create({

	initialize: function(element,options) {		
		this.element = $(element);
		this.innerDiv = this.element.down('div');
		this.options = {
			speed: 3, 
			control: true 
		};
		Object.extend(this.options, options || {});
		
		this.playScroll();
		if (this.options.control) {
			this.addObserver();
		}
	},
	
	addObserver: function() {
		this.element.observe('mouseover', this.pauseScroll.bind(this));
		this.element.observe('mouseout', this.playScroll.bind(this));	
	},
	
	playScroll: function(){
  		this.scrolling = true;
  		this.startScroll();
	},
	
	pauseScroll: function(){
		if (this.timeout) {	
			this.timeout.stop();
			this.scrolling = false;
		}
	},
	
	startScroll: function(){ 		
		if (this.scrolling) {   		
			this.timeout = new PeriodicalExecuter(function(){
	  			this.executeScroll();
	 		}.bind(this), this.options.speed/100);
		}		
	}
		
});

Le classi secondarie ereditano questi metodi dalla classe principale ed in più la estendono con metodi propri specifici per il tipo di movimento :

  • initialize(): ogni classe estesa dispone di un proprio metodo costruttore che grazie all'utilizzo della già menzionata funzione $super consente di eseguire ulteriori istruzioni senza sovascrivere il metodo costruttore della classe superiore.
  • executeScroll(): questa funzione viene richiamata ciclicamente da PeriodicalExecuter secondo l'intervallo di tempo specificato: in pratica si occupa di spostare lo scroller di 1px alla volta ad ogni esecuzione. A seconda del movimento orizzontale o verticale, si interviene sulle proprietà Javascript dell'elemento scrollLeft o scrollTop.
var horizontalMarquee = Class.create(newsMarquee,{

	initialize: function($super,elemen,options) {	
		$super(element,options);		
		this.initialWidth = this.element.getWidth();		
		this.childWidth = 0;		
		this.childs = this.innerDiv.childElements();
		this.childs[0].style.paddingLeft= this.initialWidth+'px';
		this.childs[this.childs.length-1].style.paddingRight= this.initialWidth+'px';
		this.childs.each(function(node) {			
 				this.childWidth += node.getWidth();
 			}.bind(this)
 		)		
  		this.innerDiv.style.width = this.childWidth+'px';
	}, 
	
	executeScroll: function() {
		if (this.element.scrollLeft > (this.element.scrollWidth-this.initialWidth)) {
    		this.element.scrollLeft = 0;
  		}
		this.element.scrollLeft = this.element.scrollLeft+5;		
	}

});


var verticalMarquee = Class.create(newsMarquee,{

	initialize: function($super,element,options) {
		$super(element,options);		
		this.initialHeight = this.element.getHeight();
		this.innerDiv.style.paddingTop = this.initialHeight+'px';
  		this.innerDiv.style.paddingBottom = this.initialHeight+'px';	
	}, 
	
	executeScroll: function() {
		if (this.element.scrollTop>=(this.element.scrollHeight-this.initialHeight)) {
			this.element.scrollTop=0; 	
		}
		this.element.scrollTop = this.element.scrollTop+1;	
	}

});

E' tutto, non rimane che istanziare contemponeareamente le classi estese (se vogliamo utilizzare entrambi i tipi di scroll) al caricamento della pagina e provare l'esempio:

Event.observe (window, 'load', function(){
	new horizontalMarquee('example');
	new verticalMarquee('example2');
}); 

E' possibile intervenire nel codice CSS per personalizzare la formattazione delle news nel modo che più ci aggrada; è anche possibile modificare le dimensioni dello scroller per inserire altri elementi, come delle immagini, ed ottenere un piacevole effetto di visualizzazione. Ho verificato il corretto funzionamento degli script proposti su piattaforma Windows nei seguenti browser: Internet Explorer 6, 7 ed 8, Mozilla Firefox, Opera e Safari. Il codice e gli esempi sono disponibili per il download.

Ti consigliamo anche