Gestione dei conflitti tra differenti librerie jscrip

Transcript

Gestione dei conflitti tra differenti librerie jscrip
Prototype.js
Gestione della libreria e dei conflitti
OOP in JavaScript per creare un tooltip
pag.
02
Javascript OOP
“
02
News scroller con Prototype
“
07
News Scroller: Markup HTML e regole CSS.
“
09
Breve introduzione a Prototype e a Script.aculo.us
“
13
Come evitare conflitti tra script Jquery
“
16
Evitare conflitti con altre librerie
“
20
1
OOP in JavaScript per creare un tooltip
In questo articolo vedremo come realizzare un plugin JavaScript per la creazione di tooltip,
permettendo agli utenti di visualizzare un testo descrittivo supplementare al passaggio del mouse
sopra link o altri elementi.
L’esempio sarà il pretesto per mostrare come scrivere il codice della demo utilizzano Prototype.js e
Scriptaculous, e ci offre l’occasione di esplorare l’uso e la creazione di una classe, con un approccio
object oriented più tipico di Java e C# piuttosto che di Javascript.
Grazie alla classe sarà possibile creare e personalizzare i tooltip con estrema facilità, utilizzando
anche più istanze nello stesso documento, senza dover scrivere codice inutile o ridondante.
Il codice presentato è in gran parte derivato da vari altri script che meritano una abbondante
esplorazione se utilizzate Prototype e siete interessati all’uso dei tooltip: Cooltips, Cordinc
tooltip,Tooltip-v0.2, Anatips.
Javascript OOP
È inizialmente necessario soffermarsi su alcune funzionalità che Prototype.js aggiunge a JavaScript
per la crazione delle classi. Class.create è la sintassi necessaria per la costruzione di una nuova
classe: si assegna Class.create ad una variabile che è il nome della classe.
Possiamo immaginare la classe come un contenitore che ingloba e gestisce tutte le funzioni
necessarie al nostro script, come una grande funzione (e in effetti lo è) che espone diversi metodi.
La struttura tipo del codice è più o meno questa: Class.create({ function1,
function2, function3 }).
Grazie a Prototype.js, la classe può disporre di un costruttore, cioè di un metodo che viene eseguito
quando la classe è istanziata. Tale metodo è chiamato initialize().
Appena viene creata una nuova istanza della classe la funzione initialize() verrà eseguita e
con essa anche le eventuali istruzioni contenute. Questo costruttore può accettare degli argomenti
che verranno passati nel momento in cui viene dichiarata una istanza della classe tramite la keyword
new. Un tentativo di utilizzo in questo esempio:
<a id="myButton" href="#">passami sopra con il mouse per testare l'esempio</a>
<script>
var MyClass = Class.create({
initialize : function(button, message) {
this.button
= $(button);
this.message
= message;
this.button.observe('mouseover',
this.getMessage.bindAsEventListener(this));
},
getMessage : function(e) {
this.newMessage = this.message + ' ( attivata dall\'evento ' + e.type + '
sul link con id ' + Event.element(e).id + ' )';
this.showMessage();
},
showMessage : function(){
this.button.up().insert(this.newMessage);
}
});
new MyClass('myButton', 'prova inserimento Tooltip');
</script>
2
Da osservare attentamente l’uso della keyword this, che permette rapidi riferimenti tra elementi e
metodi della classe: in particolare, Prototype estende l’oggetto function con due metodi,
bind() e bindAsEventListener(), che consentono alla funzione a cui sono applicati di
poter passare un argomento per un’esecuzione ulteriore, senza perdere il riferimento che lo associa
a un determinato oggetto. In particolare bindAsEventListener() viene utilizzato per passare
al gestore l’oggetto event come primo argomento, e in modo cross-browser per assicurare la
compatibilità anche con i browser più datati…
Dalla teoria alla pratica
Vediamo subito in un altro esempio un semplice script per i tooltip, già funzionante, con
caratteristiche essenziali (come dire, il minimo indispensabile). Ecco il codice necessario da
includere nell’head della nostra pagina:
<script type="text/javascript" src="js/prototype.js"></script>
<script type="text/javascript" src="protip-simple.js"></script>
<script type="text/javascript">
var startDemo = function() {
$$("a.demo").each( function(node) {
new Protip(node);
});
}
document.observe('dom:loaded', startDemo);
</script>
<link href="css/protip.css" rel="stylesheet" type="text/css" />
Notare il costrutto new Protip() con cui viene creata una istanza della classe al caricamento
della pagina (non appena è disponibile l’albero del DOM). Il file CSS contiene le regole necessarie
alla personalizzazione dell’aspetto del tooltip: è necessario assegnare un posizionamento assoluto e
un valore di z-index sufficientemente elevato. Lo script percorre il documento alla ricerca di tutti i
tag <a> con classe “demo” e per ognuno invoca una nuova istanza della classe Protip. Come
abbiamo visto nell’esempio precedente, questa classe è costituita da varie funzioni figlie ognuna
adibita ad un diverso compito:
• initialize(): è il costruttore che accetta come argomento l’elemento su cui vogliamo
mostrare il tooltip. Di fatto attiva il nostro script invocando le funzioni addObservers()
e setupProtip().
• addObservers(): semplicemente questa funzione cattura gli eventi del mouse
dell’utente sull’elemento. Gli eventi che ci interessano, sono tre: mouseover, mouseout e
mousemove; al verificarsi di ognuno è associata una funzione nel nostro script. Fare
attenzione al binding delle funzioni e all’uso del metodo di Prototype event.observe.
• setupProtip(): è la funzione che genera il codice HTML del tooltip e lo inietta
attraverso il DOM. In pratica, per ogni tag <a> con classe “demo” inserisce dinamicamente
e temporaneamente nasconde un <div> (che costituisce il tooltip). Il contenuto del tooltip è
preso direttamente dall’attributo title del tag <a>. Anche in questa funzione vengono
utilizzati dei metodi proprietari di Prototype (element.insert(),
element.update() e lo stesso costrutto new element()).
• showProtip(): viene mostrata il tooltip in seguito all’evento mouseover dell’utente
sull’elemento (element.show()).
3
• hideProtip(): di conseguenza nasconde il tooltip in seguito a un evente mouseout dal
relativo elemento (element.hide()).
• moveProtip(): questa funzione intercetta il movimento del mouse sopra l’elemento e
regola il posizionamento del tooltip. Le coordinate del mouse vengono ottenute grazie ai
metodi di Prototype Event.pointerX() ed Event.pointerY() e a al passaggio
dell’oggetto event catturato dalla funzione addObservers().
Ecco il codice codice della classe Protip contenuto in protip-simple.js:
var Protip = Class.create({
initialize: function(element){
this.element
= $(element);
this.addObservers();
this.setupProtip();
},
setupProtip: function() {
this.content
= this.element.readAttribute('title');
this.element.title = '';
this._protip
= new Element('div',
{'class':'protip'}).update(this.content);
$$('body')[0].insert(this._protip.hide());
},
addObservers: function() {
Event.observe(this.element, "mouseover", this.showProtip.bind(this));
Event.observe(this.element, "mouseout", this.hideProtip.bind(this));
Event.observe(this.element, "mousemove",
this.moveProtip.bindAsEventListener(this));
},
showProtip: function() {
this._protip.show();
},
hideProtip: function() {
this._protip.hide();
},
moveProtip: function(event){
this.mouseX = Event.pointerX(event);
this.mouseY = Event.pointerY(event);
this._protip.setStyle({ top:this.mouseY + 20 + "px", left:this.mouseX + 15 +
"px" });
}
});
Un codice più versatile con le opzioni
Lo script sopra esaminato è un esempio di ordine e modularità ma ancora non rende merito delle
effettive possibilità dell’uso delle classi Javascript di Prototype. Un code-pattern tipico nella
creazione di plugin con Prototype prevede il passaggio nella funzione costruttore initialize()
di un secondo argomento (options) che può contenere un’hash di parametri opzionali (coppie
chiave/valore). Se le coppie sono presenti, vengono utilizzati i valori specificati nella dichiarazione
4
della nuova istanza della classe, altrimenti vengono presi i valori di default. In questo modo è
possibile modificare i parametri dello script senza dover intervenire in più parti: con poche righe di
codice è possibile creare diverse istanze personalizzate della classe nello stesso documento. Per
attuare queste migliorie nello script precedente è quindi necessario modificare la funzione
initialize() e inserire la funzione setOptions(), che può essere considerata come il
pannello di controllo della classe, permettendo di configurare le tooltip in base alle diverse
preferenze.
initialize: function(elemetn,options){
this.element = $(element);
this.setOptions(options);
this.addObservers();
this.setupProtip();
},
setOptions: function(options) {
this.options = {
maxWidth: '',
offsetX: 15,
offsetY: 20,
opacity: .75,
appearDuration: 0.5,
hideDuration: 1.0
};
Object.extend(this.options, options || {});
}
Le varie opzioni ora disponibili consentono di integrare nello script delle funzionalità aggiuntive.
Nel caso di questo secondo esempio è stato incluso anche Scriptaculous (per la precisione sono
sufficienti scriptaculous.js ed effect.js) per applicare un effetto fade all’apparizione della tooltip
(Effect.Appear ed Effect.Fade). I parametri opzionali opacity, appearDuration e
hideDuration servono a personalizzare questo effetto. Vediamo quindi come sono cambiate le
funzioni showProtip() e hideProtip():
showProtip: function() {
this._protip.currentEffect && this._protip.currentEffect.cancel();
this._protip.currentEffect = new Effect.Appear(this._protip, { duration:
this.options.appearDuration, to: this.options.opacity });
},
hideProtip: function() {
this._protip.currentEffect && this._protip.currentEffect.cancel();
this._protip.currentEffect = new Effect.Fade(this._protip, { duration:
this.options.hideDuration });
}
Tramite il parametro opzionale maxWidth possiamo limitare la larghezza del tooltip, mentre con
offsetX/offsetY regoliamo la distanza del tooltip dal puntatore del mouse. Le ultime modifiche al
codice riguardano quindi le funzioni setupProtip() e moveProtip() con cui si assicura un
corretto posizionamento della tooltip anche rispetto alle dimensioni orizzontali della finestra del
browser. Per ottenere la larghezza del tooltip viene utilizzato il metodo di Prototype
element.getWidth(), che ci restituisce un valore espresso in px.
setupProtip: function() {
this.content
= this.element.readAttribute('title');
this.element.title = '';
this._protip
= new Element('div',
{'class':'protip'}).update(this.content);
5
$$('body')[0].insert(this._protip.hide());
if (this.options.maxWidth!='' &&
this._protip.getWidth()>this.options.maxWidth) {
this.protipWidth = this.options.maxWidth;
} else {
this.protipWidth = this._protip.getWidth();
}
},
moveProtip: function(event){
this.mouseX = Event.pointerX(event);
this.mouseY = Event.pointerY(event);
if((this.protipWidth+this.mouseX)>=(Element.getWidth(document.body)this.options.offsetX)) {
this.mouseX = (this.mouseX - this.protipWidth) - 2*this.options.offsetX;
}
this._protip.setStyle({ top:this.mouseY + this.options.offsetY + "px",
left:this.mouseX + this.options.offsetX + "px" });
}
Ecco l’esempio completo in opera: naturalmente è possibile applicare la tooltip non solo su dei link,
ma anche su div, input, e su qualunque altro tag provvisto di un attributo title. La compatibilità
cross-browser degli script presentati è buona: Firefox, Safari, Chrome e le varie versioni di IE (6, 7
e 8). Proprio per correggere alcuni problemi del framework con l’ultima versione del browser
Microsoft, consiglio di utilizzare da subito la bleeding-edge version di Prototype (1.6.1 RC2). In
finale il link per il download di tutti gli esempi.
Riguardo alla parte teorica trattata nell’articolo, non è stata affrontata l’ereditarietà delle classi in
Prototype, in particolare la possibilità di creare delle sottoclassi a partire da una classe principale e
le funzionalità di Object.extend e Class#addMethods, con cui possiamo creare metodi
“on the fly” dove necessario: sono strumenti molto utili e potenti per lo sviluppatore. Poco male
comunque, mi riprometto di approfondire presto questi apetti in un nuovo articolo: stay tuned!
6
News scroller con Prototype
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>");
}
})
7
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.
8
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;
width:700px;
height:30px;
overflow:hidden;
}
#example p {
padding:0 50px;
margin:0;
width:auto;
display:inline;
float:left;
white-space:nowrap;
line-height:30px;
}
/* scroller verticale */
#example2 {
border:1px solid #000;
background:#E1F0F9;
width:300px;
height:150px;
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
9
•
•
•
•
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();
10
}.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.scrollWidththis.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.scrollHeightthis.initialHeight)) {
this.element.scrollTop=0;
11
}
}
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.
12
Breve introduzione a Prototype e a Script.aculo.us
In questa brevissima guida introduco sinteticamente
• Prototype e
• Script.aculo.us
Prototype è decisamente una libreria molto potente e sebbene io tenda a preferire JQuery per la sua
semplicità, Prototype non può non essere considerato come uno dei migliori framework javascript
disponibili.
Per chi non sa cos’è un framework sappia che sono collezioni di librerie, componenti, e altre risorse
costruite sopra una data piattaforma per semplificare lo sviluppo di un’applicazione su quella
piattaforma.
Per chi ha letto Effective UI, la definizione è
The terms “platform” and “framework” are often used inconsistently and interchangeably. We
consider platforms to be the foundational basis for a product, including the language used to code it.
Frameworks are collections of libraries, components, and other resources built on top of a given
platform to simplify the development of applications on that platform.
Jonathan Anderson, John McRee, Robb Wilson
In questo modo non c’è la necessità di “reinventare la ruota”, come dicono gli anglosassoni, e si può
utilizzare del buon codice, molto performante e ben testato per ovviare alle esigenze più comuni.
Prototype è una libreria abbastanza invadente. Essendo javascript, una simulazione di un
linguaggio ad oggetti, non ha classi reali dove vengono definiti i tipi primitivi e Prototype tra le sue
funzionalità ha la possibilità di estendere i tipi primitivi di javascript (es. l’oggetto String,
l’oggetto Array, ecc..).
Questo rende Prototype prepotente rispetto, ad altre librerie che qualora si trovassero ad essere
richiamate all’interno della stessa pagina potrebbero non funzionare più correttamente.
Prototype permette sostanzialmente di:




agire sul contenuto manipolando il DOM
estendere le funzionalità dei tipi nativi
gestire gli eventi
utilizzare la tecnologia AJAX
13
 gestire la UI nel modo più efficiente
 estendere se stessa con nuovi moduli
Il contenuto html ovvero gli elementi della pagina vengono richiamati attraverso dei selettori.
Ci sono 3 tipi di selettori
 $() -> Ritorna un elemento o una serie di elemento attraverso ID dell’elemento
 $$() -> Ritorna un elemento o una serie di elemento attraverso un espressione CSS3
 $F() -> Ritorna valori di un elemento form
Una volta selezionato l’elemento html, Prototype da la possibilità di:
1.
2.
3.
4.
5.
6.
Manipolare il contenuto
Navigare nel DOM (spostarsi con facilità nel DOM)
Modificare le proprietà dello stile CSS
Gestire gli eventi
Mantenere un aspetto uniforme su più piattaforme (cross browsing)
Miscellanee (altre caratteristiche)
La sintassi con cui si utilizza Prototype è abbastanza semplice:
document.observe(“dom:loaded”, function(){
$(“myLink”).observe(“click”, function(){
//fa qualcosa
});
});
Con
document.observe(“dom:loaded”,
function(){
sto dicendo: appena la pagina è stata caricata, esegui queste istruzioni
$(“myLink”).observe(“click”,
//fa qualcosa
});
function(){
Non si tratta che di un listener, on metodo applicato ad un oggetto che permette di intercettare un
evento. In questo caso si tratta dell’evento “Click”
Quando l’elemento “myLink” viene cliccato viene eseguita una funzione, in questo caso //fa
qualcosa
Scriptaculous è una libreria che si basa ed estende Prototype e serve per aggiungere
animazione e maggiore controllo della UI alle applicazioni web.
Basandosi su Prototype implica che per funzionare necessita di essa.
Scaricando lo zip dal sito ufficiale di Script.aculo.us, all’indirizzo http://script.aculo.us/downloads,
troverai al suo interno diverse cartelle:
• Lib, contiene Prototype
• Src, contiene i file di Script.aculo.us
14
Diversamente da Prototype, Script.aculo.us è suddivisa in diversi file. Ogni file serve ad estendere
una funzionalità specifica.
Esiste poi il file Scriptaculous.js che racchiude tutte le funzionalità, ma è sempre consigliato di
utilizzare solo le librerie di cui necessiti, per agevolare la fruizione dei tuoi contenuti ai tuoi utenti.
Ecco cosa può fare per te Script.aculo.us
• EFFETTI ANNIMATI (effects.js) Fornisce 25 effetti visuali diversi e 7 transizioni
• DRAG & DROP (dragdrop.js) Rende gli elementi mobili, definisce are snap, e permette il
sorting degli elementi
• IN PLACE EDITING (controls.js) Crea autocompleter e oggetti modificabili attivabili al
click
• CONTROLLI TENDINA (slider.js) Permette di creare tendine verticali e orizzontali e altri
oggetti
• DOM COSTRUTTORE (builder.js) Semplifica la creazione di elementi del DOM e migliora
le performance di cross browsing
• AUDIO (sound.js) Permette di gestire suoni senza la necessità di plugin flash
15
Come evitare conflitti tra script Jquery
In questa breve guida vi svelero’ un piccolo trucco per far “convivere” diversi script Jquery
nella stessa pagina o usare contemporamente le librerie Prototype.
Molte volte infatti, data la chiamata ricorrente di funzioni e variabili col $, gli script Jquery vanno
letteralmente in conflitto rendendo il sito instabile o addirittura non funzionando correttamente.
Ricordiamo intanto che, per chi non lo sapesse la funzione $() presente in quasi tutti gli script e’
solo un ALIAS. Per far funzionare tutti gli script bastera’ quindi usare la pratica e comoda
modalita’ “no conflict()” che permette di sostituire il dollaro con la parola “jQuery”. In questo
modo viene differenziata la variabile (o la funzione) dalle precedenti usate nella pagina web.
1 <script>
2 jQuery.noConflict();
3 // Usa jQuery con jQuery(...)
4 jQuery(document).ready(function(){
5 jQuery("#foto").show("slow");
6 });
7 // Codice di una libreria con $(...)
8 // ...
9 </script>
In questo modo e’ possibile, come vedete chiaramente dall’esempio sovrastante, usare due codici jQuery che altrimenti
andrebbero in conflitto. La soluzione e’ utile anche (e sopratutto) se state usando contemporaneamente anche le librerie
Prototype (anch’esse usano le variabili $ ).
Esistono anche ulteriori metodi, ma ho pensato di illustrarvi quello piu’ semplice da attuare. Per
altri metodi vi rimando alla pagina ufficiale.
NOTA PERSONALE: Ho trovato molto utile questa soluzione sopratutto usando Joomla e
Virtualmart. Il template da me costruito aveva delle animazioni con jQuery che non facevano
reagire il carrello quando si cliccava su “aggiungi”.
16
jQuery.noConflict()
Description: Relinquish jQuery's control of the $ variable.
jQuery.noConflict( [removeAll ] )
version added: 1.0removeAll
Type: Boolean
A Boolean indicating whether to remove all jQuery variables from the global scope (including
jQuery itself).
Many JavaScript libraries use $ as a function or variable name, just as jQuery does. In jQuery's case, $ is just an alias
for jQuery, so all functionality is available without using $. If you need to use another JavaScript library alongside
jQuery, return control of $ back to the other library with a call to $.noConflict(). Old references of $ are saved
during jQuery initialization; noConflict() simply restores them.
If for some reason two versions of jQuery are loaded (which is not recommended), calling
$.noConflict( true ) from the second version will return the globally scoped jQuery
variables to those of the first version.
1 <script src="other_lib.js"></script>
2 <script src="jquery.js"></script>
<script>
3 $.noConflict();
4 // Code that uses other library's $ can follow here.
5 </script>
6
This technique is especially effective in conjunction with the .ready() method's ability to alias
the jQuery object, as within callback passed to .ready() you can use $ if you wish without fear
of conflicts later:
1 <script src="other_lib.js"></script>
2 <script src="jquery.js"></script>
3 <script>
4 $.noConflict();
jQuery( document ).ready(function( $ ) {
5
// Code that uses jQuery's $ can follow here.
6 });
7 // Code that uses other library's $ can follow here.
8 </script>
9
If necessary, you can free up the jQuery name as well by passing true as an argument to the
method. This is rarely necessary, and if you must do this (for example, if you need to use multiple
versions of the jQuery library on the same page), you need to consider that most plug-ins rely on the
presence of the jQuery variable and may not operate correctly in this situation.
17
Examples:
Example: Map the original object that was referenced by $ back to $.
1
2
3
4
5
jQuery.noConflict();
// Do something with jQuery
jQuery( "div p" ).hide();
// Do something with another library's $()
$( "content" ).style.display = "none";
Example: Revert the $ alias and then create and execute a function to provide the $ as a
jQuery alias inside the function's scope. Inside the function the original $ object is not
available. This works well for most plugins that don't rely on any other library.
1
2
3
4
5
6
7
8
jQuery.noConflict();
(function( $ ) {
$(function() {
// More code using $ as alias to jQuery
});
})(jQuery);
// Other code using $ as an alias to the other library
Example: Create a different alias instead of jQuery to use in the rest of the script.
1
2
3
4
5
6
7
var j = jQuery.noConflict();
// Do something with jQuery
j( "div p" ).hide();
// Do something with another library's $()
$( "content" ).style.display = "none";
Example: Completely move jQuery to a new namespace in another object.
var dom = {};
1 dom.query = jQuery.noConflict( true );
2
Result:
1
2
3
4
5
6
7
8
// Do something with the new jQuery
dom.query( "div p" ).hide();
// Do something with another library's $()
$( "content" ).style.display = "none";
// Do something with another version of jQuery
jQuery( "div > p" ).hide();
18
Example: Load two versions of jQuery (not recommended). Then, restore jQuery's globally
scoped variables to the first loaded jQuery.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery.noConflict demo</title>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<div id="log">
<h3>Before $.noConflict(true)</h3>
</div>
<script src="//code.jquery.com/jquery-1.6.2.js"></script>
<script>
var $log = $( "#log" );
$log.append( "2nd loaded jQuery version ($): " + $.fn.jquery + "<br>" );
// Restore globally scoped jQuery variables to the first version loaded
// (the newer version)
jq162 = jQuery.noConflict( true );
$log.append( "<h3>After $.noConflict(true)</h3>" );
$log.append( "1st loaded jQuery version ($): " + $.fn.jquery + "<br>" );
$log.append( "2nd loaded jQuery version (jq162): " + jq162.fn.jquery + "<br>" );
</script>
</body>
</html>
Demo:
Before $.noConflict(true)
2nd loaded jQuery version ($): 1.6.2
After $.noConflict(true)
1st loaded jQuery version ($): 1.10.2
2nd loaded jQuery version (jq162): 1.6.2
19
Evitare conflitti con altre librerie
Nella creazione del nostro portale web potrebbe essere necessario utilizzare diverse librerie, magari
perchè una offre dei vantaggi che l'altra non offre o più semplicemente perchè troviamo un plugin
ben fatto che utilizza quella libreria piuttosto che un'altra.
Non sempre l'utilizzo contemporaneo di più librerie (o framework) differenti è ammesso, in quanto
potrebbero andare in conflitto. Un problema piuttosto comune è quello che riguarda il nome dei
metodi: infatti se due o più librerie implementano tramite lo stesso metodo due operazioni diverse,
il sistema risulterebbe instabile e non si avrebbe certezza su quale delle operazioni viene
effettivamente eseguita.
Con jQuery il problema nasce soprattutto dal fatto che molte librerie utilizzano il simbolo del
dollaro come funzione o come variabile. Per questo motivo jQuery mette a disposizione la funzione
noConflict() la quale esclude l'utilizzo del dollaro per jQuery "liberandolo" per altre librerie. In
sostanza jQuery "rinuncia" al suo alias $().
Una premessa importante da fare prima di proseguire è la seguente: le tecniche di risoluzione dei
conflitti che vedremo risolvono i problemi tra jQuery ed altre librerie che utilizzano il simbolo del
dollaro, ma non tra altre due librerie che, per altri e svariati motivi, interferiscono tra loro.
Il primo sistema che viene utilizzato è quello di non utilizzare $() come alias, ma di scrivere sempre
jQuery(). Vediamo un esempio:
<script>
jQuery.noConflict();
// Usa jQuery con jQuery(...)
jQuery(document).ready(function(){
jQuery("#foto").show("slow");
});
// Codice di una libreria con $(...)
// ...
</script>
Questo metodo può essere tuttavia poco pratico per via della riscrittura della parola "jQuery".
Si può quindi decidere di cambiare l'alias di jQuery impostandone uno personale, ovviamente
diverso dal dollaro. Un esempio è utilizzare "$j" invece di "jQuery". Vediamo come fare:
<script>
var $j = jQuery.noConflict();
// Usa jQuery con $j(...)
$j(document).ready(function(){
$j("#foto").show("slow");
});
// Codice di una libreria con $(...)
// ...
</script>
Questo metodo è molto più rapido del precedente.
Ma se non volete abbandonare il buon vecchio dollaro allora possiamo passare al terzo metodo di
risoluzione dei conflitti. Mettiamo tutto il codice di interesse nella funzione ready() di fine
caricamento del DOM e, al suo interno, possiamo continuare ad utilizzare jQuery come abbiamo
sempre fatto:
20
<script>
jQuery.noConflict();
jQuery(document).ready(function($){
$("#foto").show("slow");
});
// Codice di una libreria con $(...)
// ...
</script>
Un ulteriore soluzione, che stavolta non prevede l'utilizzo di noConflict(), è data dall'includere la
libreria jQuery prima di ogni altra libreria. In questo modo si può tranquillamente utilizzare la
funzione jQuery() e lo shortcut $() il quale resta disponibile anche per le altre librerie.
<script src="jquery.js"></script>
<script src="prototype.js"></script>
<script>
// Usa jQuery con jQuery(...)
jQuery(document).ready(function(){
$("#foto").show("slow");
});
// Codice prototype che utilizza $(...)
// ...
</script>
21