Un esempio in profondità
Indice
La missione sarà quella di creare un elemento d’interfaccia utente che presenta pezzi di contenuti divisi in diverse sezioni. Cliccando su una sezione si mostrerà un elenco di voci della sezione, cliccando su un elemento di navigazione sinistra si visualizzerà la voce nella zona del contenuto. Ogni volta che una sezione viene mostrata, la prima voce della sezione dovrebbe essere mostrata. La prima sezione deve essere riportata al caricamento della pagina.
Fase 1: Elaborazione del codice HTML- Dare un senso e lavorare comunque quando JavaScript non è disponibile.
- Fornire un DOM prevedibile a cui ci si riesca a mettere JavaScript.
- Evitare ID e classi inutili (e ci si può essere sorpresi da quanto pochi siano necessari).
Con queste linee guida in mente, cominciamo con questo codice HTML.
Codice HTML
<h1>Questo è il mio Feature Nidificato</h1>
<div id="myFeature">
<ul class="sections">
<li>
<h2><a href="/section/1">Sezione 1</a></h2>
<ul>
<li>
<h3><a href="/section/1/content/1">Sezione 1 Titolo 1</a></h3>
Il contenuto estratto dall'Item 1
</li>
<li>
<h3><a href="/section/1/content/2">Sezione 1 Titolo 2</a></h3>
Il contenuto estratto dall'Item 2
</li>
<li>
<h3><a href="/section/1/content/3">Sezione 1 Titolo 3</a></h3>
Il contenuto estratto dall'Item 3
</li>
</ul>
</li>
<li>
<h2><a href="/section/2">Sezione 2</a></h2>
<ul>
<li>
<h3><a href="/section/2/content/1">Sezione 2 Titolo 1</a></h3>
Il contenuto estratto dall'Item 1
</li>
<li>
<h3><a href="/section/2/content/2">Sezione 2 Titolo 2</a></h3>
Il contenuto estratto dall'Item 2
</li>
<li>
<h3><a href="/section/2/content/3">Sezione 2 Titolo 3</a></h3>
Il contenuto estratto dall'Item 3
</li>
</ul>
</li>
<li>
<h2><a href="/section/3">Sezione 3</a></h2>
<ul>
<li>
<h3><a href="/section/3/content/1">Sezione 3 Titolo 1</a></h3>
Il contenuto estratto dall'Item 1
</li>
<li>
<h3><a href="/section/3/content/2">Sezione 3 Titolo 2</a></h3>
Il contenuto estratto dall'Item 2
</li>
<li>
<h3><a href="/section/3/content/3">Sezione 3 Titolo 3</a></h3>
Il contenuto estratto dall'Item 3
</li>
</ul>
</li>
</ul>
</div>
Si noti che non abbiamo incluso nessun marcatore per visualizzare la sezione di navigazione o per la navigazione a item, i pezzi saranno aggiunti da jQuery dal momento che funziona solo con jQuery, gli utenti senza JavaScript abilitato riceveranno una bella markup semantica. – Se c’è qualcosa di sorprendente o di confusione in questo HTML, sarebbe un ora buon momento per leggere qualcosa su POSH (acronimo di plain-old semantic HTML) e aumento progressivo (progressive enhancement).
myFeature.init()
verrà eseguito su$(document).ready()
. E si accede dal codice HTML per iniziare a creare una interfaccia utente in JavaScript se èabilitato.myFeature.buildSectionNav()
viene chiamato damyFeature.init()
. Ci vorrà un oggetto jQuery che contiene tutte le sezioni dal codice HTML per costruire la navigazione in alto. Si legheranno i gestori dei click per ogni voce di navigazione in alto in modo che cliccando su di essi si mostra la sezione appropriata.myFeature.buildItemNav()
viene chiamato damyFeature.showSection()
. Ci vorrà un oggetto jQuery che contiene tutti gli elementi connessi con le sezioni del codice HTML, e usarli per costruire la navigazione laterale. Si legheranno i gestori click per elementi di navigazione sul lato in modo che cliccando su di essi mostrerà il contenuto appropriato.myFeature.showSection()
viene chiamato quando l’utente fa click su un elemento dalla barra di navigazione in alto. Userà la voce di navigazione dove è cliccato per capire quale sezione mostrare dal codice HTML.myFeature.showContentItem()
viene chiamato quando l’utente fa click su un elemento nella barra di navigazione laterale. Userà la voce di navigazione che è cliccato per capire quale elemento di contenuto visualizzare dal codice HTML
Ci sarà anche da fare uno spazio per le proprietà di configurazione, myFeature.config()
, che sarà un luogo unico per l’impostazione dei valori di default e per non disperdendoli in tutto il codice. Ci includiamo la capacità di sovrascrivere le impostazioni di default quando si definisce il metodo myFeature.init()
.
var myFeature = {
'config' : { },
'init' : function() { },
'buildSectionNav' : function() { },
'buildItemNav' : function() { },
'showSection' : function() { },
'showContentItem' : function() { }
};
myFeature.config()
e scrivere il metodo myFeature.init()
:
Dettagi del codice js
var myFeature = {
'config' : {
// il contenitore predefinito è #myFeature
'container' : $('#myFeature')
},
'init' : function(config) {
// fornisce la configurazione
// personalizzata tramite init()
if (config && typeof(config) == 'object') {
$.extend(myFeature.config, config);
}
// Crea e/o salva alcuni elementi nella cache
// dal DOM da utilizzare lungo tutto il codice
myFeature.$container = myFeature.config.container;
myFeature.$sections = myFeature.$container.
// cerca e seleziona solo i figli prossimi!
find('ul.sections > li');
myFeature.$section_nav = $('<p/>')
.attr('id','section_nav')
.prependTo(myFeature.$container);
myFeature.$item_nav = $('<p/>')
.attr('id','item_nav')
.insertAfter(myFeature.$section_nav);
myFeature.$content = $('<p/>')
.attr('id','content')
. insertAfter(myFeature.$item_nav);
// costruisce la sezione a livello nav
// e forza il "click" del suo primo elemento
myFeature.buildSectionNav(myFeature.$sections);
myFeature.$section_nav.find('li:first').click();
// nasconde tutte le sections dell'HTML dalla vista
myFeature.$container.find('ul.sections').hide();
// prende nota che l'inizializzazione è completata,
// ma non abbiamo assolutamente bisogno di questo
// per questa iterazione, ma può tornare utile
myFeature.initialized = true;
},
//Poi creare il metodo myFeature.buildSectionNav():
'buildSectionNav' : function($sections) {
// itera la lista delle sections trovate
$sections.each(function() {
// Ottiene la section
var $section = $(this);
// crea una lista di item nella parte
// del menù di navigazione
$('<li/>')
// usa il testo del primo elemento h2
// della section come testo per il
// menù di navigazione
.text($section.find('h2:first').text())
// aggiunge alla lista di items del
// menù di navigazione
.appendTo(myFeature.$section_nav)
// usa il metodo data() per memorizzare
// un riferimento della section originale
// sulla nuova lista di items
.data('section', $section)
// vincola il comportamento del click
// sui nuovi items recentemente creati
// in modo da visualizzara la section
.click(myFeature.showSection);
});
},
// poi si crea il metodo myFeature.buildItemNav():
'buildItemNav' : function($items) {
// itera su ogni item fornito
$items.each(function() {
// Ottiene l'item
var $item = $(this);
// crea una lista di elementi
// per navigare attraverso gli item
$('<li>')
// usa il testo del primo h3
// dell'item come testo per
// la voce del menù di navigazione
.text($item.find('h3:first').text())
// aggiunge l'elemento dell'elenco
// alla voce di navigazione
.appendTo(myFeature.$item.nav)
// Utilizza data() per memorizzare
// un riferimento per l'elemento originale
// della nuova voce dell'elenco
.data('item', $item)
// vincola il comportamento del click per
// l'elemento della lista appena creata
// in modo che visualizzi il contenuto
// dell'elemento
.click(myFeature.showContentItem);
});
},
// infine, si scrivono i metodi per visualizzare
// le sezioni e gli elementi nel contenitore:
'showSection' : function() {
// cattura la voce dall'elenco dove è stato cliccato
var $li = $(this);
// elimina il nav a sinistra e l'area dei contenuti
myFeature.$item_nav.empty();
myFeature.$content.empty();
// ottiene l'oggetto jQuery dal codice
// HTML originale, memorizzato utilizzando
// data() in buildSectionNav()
var $section = $li.data('section');
// contrassegna la voce dell'elenco selezionato
// come 'current' e rimuovere l'indicatore 'current'
// dai suoi fratelli
$li.addClass('current')
.siblings().removeClass('current');
// trova tutti gli elementi correlati alla sezione
var $items = $section.find('ul li');
// costruisce la voce di navigazione per la sezione
myFeature.buildItemNav($items);
// forza il "click" del primo elemento della lista
// di voci di navigazione nella sezione
myFeature.$item_nav.find('li:first').click();
},
'showContentItem' : function() {
var $li = $(this);
// contrassegna la voce dell'elenco cliccato
// come 'current' e rimuove l'indicatore 'current'
// dai suoi fratelli
$li.addClass('current')
.siblings().removeClass('current');
// ottiene l'oggetto jQuery dal codice HTML
// originale che si è memorizzato utilizzando
// i dati durante buildContentNav()
var $item = $li.data('item');
// usa l'item dell'HTML per popolare
// l'area dei contenuti
myFeature.$content.html($item.html());
}
};
Non resta che chiamare il metodo myFeature.init()
:
$(document).ready(myFeature.init);
Potete vedere questo esempio scaricando (con un po’ di CSS per renderla presentabile) un file compreso da qui.
var myFeature = {
'config' : {
'container' : $('#myFeature'),
// funzione configurabile per ottenere
// il contenuto dell'articolo da un URL
'getItemURL' : function($item) {
return $item.find('a:first').attr('href');
}
},
'init' : function(config) {
// rimane lo stesso
},
'buildSectionNav' : function($sections) {
// rimane lo stesso
},
'buildItemNav' : function($items) {
// rimane lo stesso
},
'showSection' : function() {
// rimane lo stesso
},
'showContentItem' : function() {
var $li = $(this);
$li.addClass('current').
siblings().removeClass('current');
var $item = $li.data('item');
var url = myFeature.config.getItemURL($item);
// myFeature.$content.html($item.html());
myFeature.$content.load(url);
}
};
Bisogno di maggiore flessibilità? Ci sono molte altre cose che, volendo, è possibile impostare (e quindi sovrascrivere) se si vuole veramente rendere flessibile il nostro Oggetto Letterale. Ad esempio, è possibile specificare in myFeature.config
il modo di trovare ed elaborare il testo del titolo di ogni elemento nel nemù di navigazione a sinistra.
var myFeature = {
'config' : {
'container' : $('#myFeature'),
// specifica il selettore predefinito
// per trovare il testo da usare
// in ogni elemento della voce nav
'itemNavSelector' : 'h3',
// specifica un callback predefinit
// per "processare" l'oggetto jQuery
// restituito dal selettore itemNavTex
'itemNavProcessor' : function($selection) {
return 'Anteprima di ' +
$selection.eq(0).text();
}
},
'init' : function(config) {
// rimane lo stesso
},
'buildSectionNav' : function($sections) {
// rimane lo stesso
},
'buildItemNav' : function($items) {
$items.each(function() {
var $item = $(this);
// usa il selettore e processore
// dal cofig per ottenere il testo
// di ogni elemento della voce nav
var myText = myFeature.config.itemNavProcessor(
$item.find(myFeature.config.itemNavSelector)
);
$('<li/>')
// usa la nuova variabile
// come testo per la voce nav
.text(myText)
.appendTo(myFeature.$item_nav)
.data('item', $item)
.click(myFeature.showContentItem);
});
},
'showSection' : function() {
// rimane lo stesso
},
'showContentItem' : function() {
// rimane lo stesso
}
};
Dopo aver aggiunto è l’oggetto per configurazione di default, è possibile ignorare quando si chiama myFeature.init()
:
$(document).ready(function() {
myFeature.init({ 'itemNavSelector' : 'h2' });
});
Oltre lo scopo di questo articolo (che sarebbe interessante vedere come è molto più semplice intervenire con il modello dell’Oggetto Letterale) è il seguente: Invertire il pulsante Indietro dal suo senso attraverso le schede usando l’estensione history
per jQuery. Lascio come esercizio al lettore.
Vi incoraggio a dare a questo modello una prova la prossima volta che vi ritroverete a scrivere più di alcune righe di codice in JavaScript – questo costringe a pensare attraverso gli elementi e i comportamenti che costituiscono una caratteristica complessa o di interazione. Una volta diventati esperti, fornisce una base solida per estendere e riutilizzare il codice.
Approfondimetni
Ancora nessun commento