FAQ.js

codice e script della pagina delle FAQs


Con un paio di anni di ritardo (sigh), pubblico il codice utilizzato per gli slide show/hide nella pagina FAQs.

Non è il migliore dei metodi, ma non sono un programmatore (sebbene in un mio passato sia stato iscritto alla facoltà di Informatica, non avendo però avuto successo e voglia di continuare) ed è già tanto che sia riuscito in quella che per un profano è un’impresa titanica.

Funzionamento

In maniera molto sintetica, la pagina FAQs richiama uno “snippet” di codice PHP.

A tal proposito, io utilizzo PHP Snippets; esistono decine di plugin di questo tipo, molti dei quali hanno una pagina di gestione dal pannello di WordPress in cui inserire i propri snippets di codice, ma io preferisco questo perché mi permette di creare dei file nella forma SNIPPET_NAME.snippet.php da richiamare mediante la su citata forma [SNIPPET_NAME].

In una directory del mio sito (cui si accede via FTP), chiamata appunto snippets, si inseriscono i vari frammenti di codice che si vogliono utilizzare; ne utilizzo diversi, ciascuno per ognuna o quasi le pagine di questo sito (ma appena avrò il tempo di capire come si fa li trasformerò da pagine a funzioni più complesse in modo da non dover ogni volta modificare i file in questione da un’applicazione esterna invece di poterli aggiornare direttamente dall’interfaccia di modifica di pagine e articoli di WordPress).

File utilizzati

I file in esame saranno 4:

Pagina principale

La pagina FAQs contiene una sola riga:

[ FAQs ]

Si noti che nella linea di codicesono presenti degli spazi attorno al nome dello snippet, per evitare che il codice venga eseguito e stampato a video.

Lo snippet FAQs.snippet.php è pertanto così strutturato:

<head>

  <title>FAQs</title>

  <!-- variables definitions to use in the document -->
  <?php $path = "http://PhysicalTraining.it/resources/"; ?>

  <!-- importing documents -->

  <!-- CSS style sheets -->
  <!-- document specific style sheets -->
  <link rel="stylesheet" href="<?php echo $path ?>css/disclaimer.css" />

  <!-- Javascript scripts -->
  <!-- Zepto | jQuery scripts -->
  <script src="<?php echo $path ?>js/jQuery/jquery-2.0.3.js"></script>
  <!-- document specific scripts -->
  <script src="<?php echo $path ?>js/slide.js"></script>
  <script src="<?php echo $path ?>js/FAQ.js"></script>

</head>

<body>

  <div id="FAQs"><!-- begin of main container -->
    <p>In questa pagina troverai le risposte alle domande più frequenti sul mio lavoro.
    Se avessi altri dubbi, o non trovassi la risposta che cerchi, non esitare a <a href="/support/contacts/" title="contatti">contattarmi</a>.</p>
  
    <!-- FAQs -->
    <script type="text/javascript">
      printFAQs();
    </script>
  </div>

</body>

La pagina, come si vede, non fa altro che utilizzare PHP per assegnare una variabile da utilizzare per richiamare i file dello stile disclaimer.css e gli script jQueryslide.js e FAQ.js.

Lo style sheet

Il foglio di stile mi serve per mettere dei semplici abbellimenti alle parti che andranno a mostrarsi/nascondersi, come il capolettera, i colori (dovrei in realtà andare a mettere tutto in custom.css, il foglio di stile personalizzato per il tutto il sito), le liste, etc.

h2.section {
  margin: 12px 0 0 0;
}

h2.disclaimer {
  font: bold small-caps 18px/1.25 'Bradley';
}

h2.disclaimer a:hover {
  color: #ee4607;
}

section a:link,
section a:visited,
h2.disclaimer a:link {
  color: #463c3c;
  text-decoration: none;
}

div.spoiler {
  margin: 0 0 12px 0;
  font: normal 14px/1.5 'Hoefler';
}

div.spoiler:first-letter {
  float: left;
  letter-spacing: 6px;
  font: 48px/40px bold;
  color: rgba(88, 77, 77, .500);
}

ol {
  margin-left: 0;
}

ol li {
  list-style-position: outside;
  list-style-type: upper-roman;
}

ol li.number {
  margin: 0 0 0 48px;
  font: bold small-caps 24px/1.25 'Bradley';
}

ol li a:link {
  color: #621f1a;
}

ol li a:hover {
  color: #ee4607;
}

ol ol li {
  margin: 0 0 0 -12px;
}

ol ul li {
  list-style-type: disc;
  margin: 0 0 0 -12px;
}

.arrow {
  font-size: 12px;
}

.button,
.button:hover {
  font: small-caps bold 18px/1.0 "Bradley";
  color: #463c3c;
}

Come dicevo, va riordinato (magari addirittura eliminato, se decido di mettere tutto nel CSS personalizzato.

Gli scripts

slide.js

In realtà, all’inizio esisteva un solo file javascript, FAQ.js, contenente sia le funzioni per stampare a video la lista di FAQs divise in sezioni, con i loro nomi e contenuti, sia le funzioni di questo slide.js che, sfruttando la libreria jQuery, mostrano e nascondono i vari contenuti.

Lo script che si occupa di mostrare e nascondere il testo in questione è solo in parte opera mia.

Infatti, cercavo sul web come fare a mostrare del testo nascosto e mi sono imbattuto dapprima in nei metodi del CSS (display), poi in javascript, jQuery e in particolare ho deciso di adottare diversi metodi della libreria:

/* function showing only one answer block at time */
function slide_v1(id) {
  $('.spoiler').each(function(index) {
    if ($(this).attr('id') == id) {
      $(this).slideDown(1000);
    } else {
      $(this).slideUp(2000);
    }
  });
}

/* function showing/hiding requested answer block */
function slide_v2(id) {
  $('#' + id).animate(
    { height: 'toggle' },
    { duration: 1000 }
  );
}

/* function showing/hiding requested answer block and closing opened ones */
function slide(id) {                    // A.K.A. slide_v3
  $('.spoiler').each(function(index) {
    if ($(this).attr('id') == id) {
      $(this).animate(
        { height: 'toggle' },
        { duration: 1000 }
      );
    } else {
      $(this).slideUp(2000);
    }
  });
}

/* function to close actual showed answer with a slideUp() */
function closeId(id) {
  $('#' + id).each(function(index) {
    $(this).slideUp(2000);
  });
}

/* function to close all showed answers with a slideUp() */
function closeAll() {
  $('.spoiler').each(function(index) {
    $(this).slideUp(2000);
  });
}

Della funzione slide(Id), come si vede, sono presenti 3 versioni:

  • nella prima utilizzavo i metodi slideDownslideUp per, rispettivamente, mostrare e nascondere il testo come fosse una tendina;
  • nella seconda sostituivo entrambi con animate;
  • nella versione definitiva, infine, ho deciso per una “fusione” delle due precedenti, utilizzando animate per mostrare il testo sul click del mouse e slideUp per nascondere il testo appena mostrato ed eventualmente quello già visibile di un altra FAQ (ovvero, apro una domanda cliccandoci sopra, poi clicco su un altra e succede che si apre questa chiudendosi la precedente); una scelta stilistica personale per non lasciare aperte molte FAQ tutte insieme, magari non bellissima, ma funzionale.

FAQ.js

Ultimo, ma non per importanza (anzi, senza di questo non ci sarebbe alcun testo da mostrare!), il file che precedentemente conteneva il contenuto dello script esaminato precedentemente (slide.js)

/*
  variables definition  */

var sectionId = new Array();            //  id for every section
var FAQId = new Array();                //  id for every FAQ block (question + answer block)
var questionId = new Array();           //  id for every question
var answerId = new Array();             //  id for every answer
var sections = new Array(               //  entries sections
  "section 1",
  "section 2",
  "section 3",
  "section 4"
);
var sectionTag = new Array(         //  entries sections links
  "section 1 name",
  "section 2 name",
  "section 3 name",
  "section 4 name"
);
var questions = new Array( // entries titles, divided by sections by a NULL value
 "", // first section, section 1 name
 /* section 1 name-01 */ "title 1",
 /* section 1 name-02 */ "title 2",
 /* section 1 name-03 */ "title 3",
 "", // second section, section 2 name
 /* section 2 name-01 */ "title 4",
 /* section 2 name-02 */ "title 5",
 /* section 2 name-03 */ "title 6",
 "", // third section, section 3 name
 /* section 3 name-01 */ "title 7",
 /* section 3 name-02 */ "title 8",
 /* section 3 name-03 */ "title 6",
 "", // fourth section, section 4 name
 /* section 4 name-01 */ "title 9",
 /* section 4 name-02 */ "title 10",
 /* section 4 name-03 */ "title 11"
);
var answers = new Array( // entries contents, divided by sections by a NULL value
 "", // first section, section 1 name
 /* section 1 name-01 */ "content 1",
 /* section 1 name-02 */ "content 2",
 /* section 1 name-03 */ "content 3",
 "", // second section, section 2 name
 /* section 2 name-01 */ "content 4",
 /* section 2 name-02 */ "content 5",
 /* section 2 name-03 */ "content 6",
 "", // third section, section 3 name
 /* section 3 name-01 */ "content 7",
 /* section 3 name-02 */ "content 8",
 /* section 3 name-03 */ "content 6",
 "", // fourth section, section 4 name
 /* section 4 name-01 */ "content 9",
 /* section 4 name-02 */ "content 10",
 /* section 4 name-03 */ "content 11"
);

/* initializing sectionId, FAQId, questionId, and answerId arrays */
for (i=0; i<sections.length; i++) {
 var j = i+1;
 sectionId[i] = "S" + j;
}
var N = questions.length; // number of FAQs
var j = 1; // counter to skip blank values
for (var i=0; i<N; i++) {
 if (questions[i] != "") { // skipping blank values, filling only not blank cells
 FAQId[i] = "FAQ" + j;
 questionId[i] = "Q" + j;
 answerId[i] = "A" + j;
 j++;
 } else {
 FAQId[i] = questionId[i] = answerId[i] = "";
 }
}

/*
 function printing the single FAQ blocks one by one
*/
function printFAQ(FAQId, sectionTag) {
 // FAQs blocks
 document.writeln('<li class="number">');
 document.write('<article>');
 // questions
 document.write('<h2 id="' + questionId[FAQId] + '" class="disclaimer" style="display: inline-block;">');
 document.write('<a href="#' + questionId[FAQId] + '" onclick="javascript:slide(\'' + answerId[FAQId] + '\');">' + questions[FAQId] + '</a>');
 document.write('</h2>');
 // answers
 document.write('<div id="' + answerId[FAQId] + '" class="spoiler" style="display: none;">');
 document.write('<div>' + answers[FAQId] + '</div>');
 // close and "back to top" button
 document.write('<p align="right"><a href="#' + sectionTag + '" onclick="javascript:closeId(\'' + answerId[FAQId] + '\');"><span class="arrow">&#9700;</span>&nbsp;<span class="button">torna su</span></a></p>');
 document.write('</div>');
 document.writeln('</article>');
 document.writeln('</li>');
}


/*
 2 blocks method with href/onclick getElementById().style.display='BLOCK/NONE' to show answers
 in block #1 hiding questions divs and showing questions divs + answer divs
 in block #2 hiding questions divs + answer divs and showing again only questions divs
*/
function printFAQs_v1 () { // v1
 document.write('<ol>');
 for (i=0; i<FAQId.length; i++) {
 document.write('<div id="' + FAQId[i] + '">');
 /* open */
 document.write('<div id="' + questionId[i] + '" style="display: block;">');
 document.write('<a href="javascript:document.getElementById(\'' + answerId[i] + '\').style.display=\'block\'" onclick="document.getElementById(\'' + questionId[i] + '\').style.display=\'none\'">');
 document.write('<li class="disclaimer">' + questions[i] + '</li>');
 document.write('</a>');
 document.write('</div>');
 /* close */
 document.write('<div id="' + answerId[i] + '" style="display: none;">');
 document.write('<a href="javascript:document.getElementById(\'' + answerId[i] + '\').style.display=\'none\'" onclick="document.getElementById(\'' + questionId[i] + '\').style.display=\'block\'">');
 document.write('<li class="disclaimer">' + questions[i] + '</li>');
 document.write('</a>');
 document.write('<p class="spoiler">' + answers[i] + '</p>');
 document.write('<p align="right"><button class="button">chiudi [x]</button></p>');
 document.write('</div>');
 document.write('</div>');
 }
 document.write('</ol>');
}

/*
 2 blocks method with href function to slide up or down only the answer for selected question
 and hiding with slideUp() any other already opened answer
*/
function printFAQs_v2 () { // v2
 document.write('<ol>');
 // index for sections array
 var j = 0;
 for (i=0; i<FAQId.length; i++) {
 if (questions[i] == "") {
 // printing FAQs sections
 document.write('<section id="' + sectionId[j] + '" style="display: inline-block;">');
 document.write('<h2>' + sections[j] + '</h2>');
 document.write('</section>');
 j += 1;
 }
 else {
 // printing FAQs blocks
 printFAQ(i, sectionTag[j]);
 }
 }
 document.write('</ol>');
}

/*
 2 blocks method with href function to slide up or down only the answer for selected question
 and hiding with slideUp() any other already opened answer
*/
function printFAQs () { // A.K.A. "printFAQs_v3"
 var index = 0;
 outerloop: for (var j=0; j<sections.length; j++) {
 // printing FAQs sections
 document.write('<section id="' + sectionId[j] + '" style="display: inline-block; heigth: auto;">');
 document.write('<div id="' + sectionTag[j] + '"><h2><a href="#' + sectionTag[j] + '">' + sections[j] + '</a></h2></div>');
 document.write('<ol>');
 innerloop: for (var i=index; i<FAQId.length; i++) {
 if (questions[i] != "") {
 // printing FAQs blocks
 printFAQ(i, sectionTag[j]);
 } else {
 // alert("inizio <section> \"" + sections[j] + "\" (j = " + j + ", i = " + i + ")");
 }
 if (questions[i+1] == "") {
 index = i+1;
 break innerloop;
 }
 }
 document.write('</ol>');
 // closing section
 document.write('</section>');
 }
}

La funzione principale è printFAQs (di cui, come si vede, ho fatto 3 versioni e lasciato le prime due inutilizzate (printFAQs_v1printFAQs_v2), chiamata all’interno del primo file (FAQs.snippet.php); questa funzione ha all’interno dei cicli per costruire le sezioni (di FAQs) e pertanto le relative domande e risposte, chiamando la funzione printFAQ, che nel loop costruisce di volta in volta una coppia question/answer diversa fino al termine del ciclo.

Il ciclo esterno, outerloop, si occupa di generare le sezioni, mentre quello interno, innerloop, che genera la coppie di ogni FAQ, termina quando incontra una riga vuota nell’array questions.

Ci ho messo forse settimane a riuscire a capire come assemblare il codice, si noti per esempio la presenza di un indice denominato index oltre a ij dei due loop.

Note di fondo

Successivamente ho riusato i metodi qui descritti per altre parti del sito (un articolo che uscirà a breve per esempio), pertanto appena capirò come fare trasformerò il tutto in un plugin da richiamare con smartcode del tipo

Contenuto da visualizzare

o al limite in un gruppo di funzioni da poter richiamare con il sistema attuale ma mantenendo comunque la maggior parte del testo sulla pagina (ovvero modificabile nell’editor di WordPress).

Angelo Ruggiero
Chinesiologo, Strenght & Conditioning Coach, Personal Trainer, Wellness Coach, Educatore Psicomotorio
Personal Trainer, Sport & Fitness Trainer, Wellness Coach.

Chinesiologo, Educatore Psicomotorio e Preparatore Fisico e Atletico con esperienza decennale nello Sport, nel Fitness e nella Rieducazione Posturale e Motoria.

Ambizioso e determinato, predisposto a migliorarsi e affrontare impegni diversi ed insoliti.

Attualmente sto svolgendo attività in proprio di pianificazione e programmazione di allenamenti personalizzati per:
– attività motoria preventiva e compensativa e ​​rieducazione posturale (ginnastica correttiva)
– recupero funzionale motorio post-traumatico
– Fitness
– preparazione atletica per l’allenamento sportivo d​i tutti gli sport, individuali, di squadra e di combattimento (arti marziali), in particolare, elaborazione di programmi di allenamento personalizzati avanzati, secondo gli impegni agonistici del calendario di gara, in mesocicli, per Nuoto Master, Running, Atletica Leggera

I miei obiettivi ed interessi professionali possono così riassumersi:
– Personal Training, Fitness Training
– Allenamento Sportivo, Preparazione Atletica, Avviamento allo Sport
– Cross Training, Power Lifting, Weight Lifting
– Educazione e Rieducazione Psicomotoria
– Recupero Funzionale Motorio, Rieducazione Posturale


Angel

Informazioni su Angel

Chinesiologo, Educatore Psicomotorio, Personal Trainer, Preparatore Atletico con esperienza decennale nello Sport e nel Fitness e nella Rieducazione Posturale e Motoria, sono stato Atleta Master di Nuoto e praticato Handball per qualche anno. Ho lavorato in palestre e piscine come Istruttore di Sala Pesi e Fitness e Istruttore di Scuola Nuoto (adulti, bambini, adulti non galleggianti), ma sopratutto come Istruttore e Allenatore di Handball e Preparatore Atletico per YMCA Basketball Siderno, Tennis, Atletica Leggera e come Personal Trainer per diverse e varie esigenze. Attualmente sono impegnato in alcuni progetti e nelle attività di Personal Trainer, consulenza fitness, tecnico-sportiva e alimentare, pianificazione e programmazione di allenamenti personalizzati per fitness, nuoto master, running, atletica leggera, sollevamento pesi, CrossTraining e preparazione atletica per tutti gli sport, individuali, di squadra e di combattimento (arti marziali). Ambizioso e determinato, sempre pronto a migliorare e migliorarsi, a imparare cose nuove, affrontare impegni diversi ed insoliti, conoscere persone nuove. link