Newsgroup: it.comp.lang.javascript
Titolo: a spasso per il DOM: reperire i campi giusti

dopo un oggetto poetico spiego:
 
data questa struttura (in pseudocodice), ripetuta 4 volte anche se con n. di 
campi diverso:
 
<div id="panels">
   <p class="panel-caption"><a href="#" link di comando toggle del div 
"panel"></a></p>
   <p>Istruzioni sull'uso</p>
 
   <a href="#" title="Anteprima Math" class="icona preview"><span 
class="comando">Anteprima</span></a>
 
   <div class="panel" pannello mobile 1>
        <fieldset><legend>Proporzione</legend>
        <table class ="panel-contents">
            <tr>
                <td><input campo 1></td>
                <td><input campo 1></td>
                <td><input campo 1></td>
                <td><input campo 1></td>
            </tr>
        </table>
        </fieldset>
    </div>
    ...
 
Come cavolo si fa con jQuery a trovare i campi input relativi a questo div 
class="panel" al clic
sul link Anteprima?
Devo rilevare i valori nei campi per passarli al processore MathJax.
Ho provato una cosa cosě:
 
$(".preview").click(function() {
       var nome_input = $(".panel input").attr('name');
      alert(nome_input);
      return false;
});
 
č l'unica che mi mostra il nome del campo, ma solo del primo campo nella 
serie di un pannello
e sempre dello stesso campo anche quando clicco sui link degli altri 
pannelli.
Ho provato a usare:
$("div.panel").nextAll('input').attr("name"); e sue variazioni
$("div.panel").next('input').attr("name"); e sue variazioni
$("div.panel").closest('input').attr("name"); e sue variazioni
$(".preview").closest('input').attr("name"); e sue variazioni
$('div.panel').find('input').attr('name'); e sue variazioni
 
nulla, non ho cavato un ragno dal buco!
 
 
 
-- 
 
ciao!
 
...nixs.................................................................
nixs.lucchetto.@lagirandola.it
 
togli la ".SERRATURA." per scrivermi
........................................................................
L'uomo non smette di giocare perché invecchia,
ma invecchia perché smette di giocare.
(George Bernard Shaw)
 
 

                <td><input campo 1></td>
                <td><input campo 1></td>
                <td><input campo 1></td>
                <td><input campo 1></td>
 
chiedo scusa, qui si intende, NON chiaramente, che si tratta della serie di 
input
del pannello 1, ma con nomi diversi


 nulla, non ho cavato un ragno dal buco!
 
Qui sembra che tu sia andato un po' troppo a caso: non caverai mai un 
ragno da un buco andando a tentoni.
Leggi la documentazione, sempre, poi siam qui se qualcosa rimane oscuro.
 
In questi "tentativi" ti è sempre mancato il contesto, hai sempre 
supposto che "attr" ti restituisse i nomi di tutti gli elementi di una 
lista e hai usato metodi che non facevano quello che ti serviva.
 
A quanto leggo "next" e "nextAll" cercano tra i "fratelli" (siblings) di 
un elemento, mentre "closest" risale i gradi di parentela partendo 
dall'elemento corrente.
 
 

usato metodi che non facevano quello che ti serviva.
 
Pensavo di limitarli al contesto con next, closest e simili, ma non ci sono
riuscito.
Alla fine, nell'apprendimento del linguaggio, mi scontro sempre con lo
stesso problema principale: cercare di generalizzare una funzione
conservando la possibilità di utilizzarla solo su uno alla volta degli 
elementi
sui quali può agire.
 
Forse potrei adottare il trucco che ho usato per individuare il pannello
da aprire e che ho riportato nel precedente thread?
 
$('.panel').hide();
$('a.icona.aggiungi').click(function() {
$('#' + 
this.id
 + '_panel').toggle(400);
return false;
});


 
Primo passo in avanti:
 
con il link nella forma: <a href="#" title="Anteprima Math" class="icona 
preview" id="contents_1">
e la table: <table class="panel-contents" id="contents_1_table">
 
utilizzando la funzione:
$("a.preview").click(function() {
       var nome_input = $('#' + 
this.id
 + '_table').find('input').val();
       alert(nome_input);
       return false;
});
 
mi mostra per ogni pannello il valore immesso nel suo primo campo.
 
Se riesco a gestire tutti i campi del pannello a prescindere dal numero di 
campi presenti
ho trovato la chiave!


 });
 
Hai fatto però tre passi indietro: dopo tutto quel che ti ha scritto 
Zer0 a proposito di semplificare il codice html sei tornato a cercare 
relazioni aggiungendo id nel markup.
 
La soluzione dopotutto te l'ho data prima: anche se il markup è diverso 
è facilmente adattabile.
 
Quindi: perché non mi chiedi cosa non hai capito del post precedente 
invece che buttare tutto all'aria e cambiare strada?
 
Tu parti da un div.panel che contiene un link "anteprima" e, nidificati 
chissà dove (non ci interessa), dei campi input.
 
Tu vuoi recuperare questi input al click su "anteprima", giusto?
Poi ci facciamo anche qualcosa, ma un passo alla volta.
 
Scomponi bene il problema in testa.
 
- vuoi recuperare tutti gli input di un determinato panel (che sarà il 
nostro "contesto di ricerca", vedi mio post precedente).
 
- cosa identifica il panel? Un click su un link in esso contenuto, suo 
figlio diretto nel caso.
 
Quindi:
 
1) ottenere un riferimento al panel che servirà come contesto per la ricerca
 
- che relazione c'è tra l'elemento cliccato e il panel?
   Sono padre e figlio, quindi (lasciamo perdere jQuery):
 
     this.parentNode
 
Siamo dentro all'event handler, quindi "this" è l'elemento di cui 
ascoltiamo l'evento.
 
2) Come recuperiamo tutti gli elementi "input" discendenti di questo 
elemento?
 
     this.parentNode.getElementsByTagName("input")
 
Così hai una NodeList, che non è un Array, ma puoi ciclarla come se lo 
fosse con un classico "for".
 
     var nl = this.parentNode.getElementsByTagName("input");
     for(var i = 0, l = nl.length; i < l; i++){
         // faccio qualcosa con nl[i]
     }
 
Dimmi se ti è tutto chiaro, poi si passa a jQuery se credi.

Dimmi se ti è tutto chiaro, poi si passa a jQuery se credi.
 
Premesso che ho il link fatto così:
<a href="#" title="Anteprima Math" class="icona preview" 
id="contents_1"><span class="comando" style="width: 
50px;">Anteprima</span></a>
e che potendo cliccare sul testo "Anteprima" ho il dubbio se il PADRE del 
click è il tag SPAN, A, il fieldset o il div,
dimmi tu se ho capito e passiamo a jQuery ;-))


 dimmi tu se ho capito e passiamo a jQuery ;-))
 
Ah, ho capito il tuo dubbio.
 
Ti direi di fregartene per adesso: a come si propagano gli eventi puoi 
pensarci più avanti, ora non mettere altra carne al fuoco.
 
Quello che ti deve essere chiaro, e il motivo per cui puoi 
momentaneamente fregartene, è che tu definisci degli gestori 
("ascoltatori") di eventi e li associ a un elemento.
 
Senza jQuery la sintassi è piuttosto chiara:
 
   elemento.addEventListener("<nome evento>",
                             function(event){ ... },
                             false
   );
 
Ignora per i motivi di cui sopra il terzo parametro e fa finta che vada 
bene "false" (nei browser moderni è diventato opzionale).
 
Tu associ al tuo link un "listener" e questo viene invocato quando 
l'evento è scatenato su quell'elemento e poco ti importa che potrebbe 
essere partito da un altro elemento in esso contenuto: tu hai scelto il 
momento in cui gestirlo.
 
Nel tuo caso il gestore di eventi è associato solo ai link con classe 
"anteprima" e sei quindi sicuro che "this" all'interno della funzione si 
riferisce a uno di questi link.
 
Data la tua struttura html, il contesto in cui vogliamo fare la ricerca 
di input sarà sempre il nodo genitore dell'elemento di cui gestiamo 
l'evento.
 
In realtà potresti optare per recuperare la tabella che è "sorella" del 
link nella gerarchia, o che so altro, ma l'importante è per ora capire 
che tu puoi sfruttare le relazioni di parentela per muoverti nel DOM.
 
Nel mio post di oggi di ho fatto un esempio di ricerca con contesto 
tramite jQuery.
 
 
 

riferisce a uno di questi link.
[cut]
 
ok, ok, ora penso di aver capito e sembra funzionare:
 
    //non ricordando come JS gestisce le variabili non inizializzate per 
sicurezza la imposto io
    var tmpStr = "";
 
    //come prima, mi cerco tutti gli input
    var panelInputs= this.parentNode.getElementsByTagName("input");
 
    //ciclo per usarli
  for(var i = 0, l = panelInputs.length; i < l; i++){
 
    //ad ogni input text è associato un radio, per cui prendo solo i text
   if (panelInputs[i].type == "text") {
 
        //mi costruisco la stringa con i valori dei campi racchiusi nei 
delimitatori MathJax
        tmpStr = tmpStr + "&#96;" + panelInputs[i].value + "&#96;" + " ";
   }
  }
 
//mando in pasto alla popup che formatta in MathJax la stringa ottenuta
avviso(tmpStr);
 
FUNZIONA!
dubbio: la selezione dei soli input text non si puo fare direttamente qui?
var panelInputs= this.parentNode.getElementsByTagName("input");
Senza scomodare l'if?
 
Se mi dai l'ok passiamo al completamento:
Data la struttura di ogni <td> con l'input:
<td><input type="text" name="TERM1" size="30" maxlength="20" 
class="testo-centrato" /><span class="symbol"> : </span></td>
Vorrei prelevare, se è presente, ciò che è contenuto nello SPAN (" : " in 
questo caso) per frapporlo ai valori dei campi, ad ogni
valore il suo separatore, insomma, in modo da ricreare ciò che è scritto 
nella pagina.
Da come ho capito dovrei, raggiunto il nodo con l'input, prelevare l'html 
contenuto in SPAN, ma SPAN non è nè
parent nè child di input, però è child (anche se secondogenito (!) di 
<td>... o|O


 sul link Anteprima?
 
 
The .each() method is designed to make DOM looping constructs concise and 
less error-prone. When called it iterates over the DOM elements that are 
part of the jQuery object. Each time the callback runs, it is passed the 
current loop iteration, beginning from 0. More importantly, the callback is 
fired in the context of the current DOM element, so the keyword this refers 
to the element.
 
Suppose we had a simple unordered list on the page:
 
<ul>
    <li>foo</li>
    <li>bar</li>
</ul>
  We can select the list items and iterate across them:
 
$('li').each(function(index) {
    alert(index + ': ' + $(this).text());
});
  A message is thus alerted for each item in the list:0: foo1: barWe can 
stop the loop from within the callback function by returning 
false.
http://api.jquery.com/each/
 
 
 

 <td>... o|O
 
Qui ti ho momentaneamente perso: stavo guidandoti verso il fare qualcosa 
con l'elenco degli input ottenuti e sei passato ad altri elementi.
 
Poi sei passato, mea culpa, ad abbandonare jQuery nel tuo codice: io 
l'avevo accantonata solo perché la ritengo poco didattica, ma se la stai 
usando... nulla di male.
Anzi facciamolo, che imparo qualcosa anche io: volevo solo raccomandarmi 
che il problema rimanesse astratto dalla quella specifica libreria nella 
tua testa.
 
Non ho seguito, se non di sfuggita, i thread precedenti e per 
consigliarti bene dovrei capire che relazione c'è tra il separatore 
nello span e il valore dei campi.
 
Ricapitoliamo per ora con jQuery, poi, se hai voglia, approfondiamo un 
paio di cose.
 
Uno dei tuoi dubbi precedenti era usare la lista di input ottenuta e, 
come vedo che ti hanno consigliato, lo fai comodamente con "each".
 
Quindi:
 
$("a.preview").click(function(){
     // prendo gli input di tipo test discendenti
     // dal mio "immediato fratello" (la tabella) che è
     // il famoso secondo parametro che determina
     // il contesto di ricerca e che ti sei perso prima
     $("input[type='text']", $(this).next()).each(function(){
         // con "each" questa funzione viene eseguita
         // per ogni elemento che soddisfa la ricerca
         // "this" in questo contesto è l'elemento input
         console.log(
             "valore input: " + $(this).val() + "\n" +
             "valore span: " + $(this).next().text()
         );
     });
});
 
Scritto di getto con un occhio sulla documentazione di jQuery e l'altro 
sul bicchiere di vino, ma dovrebbe stare in piedi.
 
Facci sapere se è tutto chiaro e se vuoi approfondire perché e percome 
di come si sarebbe fatto senza.
 

 div class="panel" al clic sul link Anteprima?
 
Per prima cosa sappi che se hai un form con dei campi con lo stesso name, 
quando arrivano al server potrebbero essere tutti sovrascritti 
dall'ultimo che ha quel name. Ma poniamo il caso che ti interessi solo 
lato javascript.
 
Tutti i comandi di query di jQuery accettano un "ambito" su cui lavorare, 
e ritornano una collection di risultati (non uno solo, anche se puo` 
sembrare cosi` perche` e` trasparente agire su uno o su tutti), e tutte 
le callback impostano "this" all'oggetto che ha scatenato l'evento.
 
Detto questo:
 
$('#panels').on('click', '.panel a.preview', function() {
 
 var $preview = $(this);
 
 var $div = $preview.closest('.panel');
 
 var valori = {};
 
 $div.find('input').each(function() {
 
  valori[$(this).attr('name')] = $(this).val();
 
 });
 
 faiQuelloCheDeviFareCon(valori);
 
});
 
La prima riga e` messa cosi` perche` in questo modo puoi aggiungere altri 
".panel" al div "#panels" senza dover riagganciare la gestione degli 
eventi.
 
Bye.
 
P.S.: Il $ davanti alle variabili $preview e $div e` una convenzione che 
ho adottato per dire "questo e` un oggetto jQuery", e non un normale 
elemento del DOM o una variabile). Non e` obbligatorio.
 

 sul bicchiere di vino, ma dovrebbe stare in piedi.
 
Dovrebbe??? E meno male che non sei pratico di jq!
Allora io a confronto sono la metà di quello studente che
veniva da tuo padre! :-))
FUNZIONA!
 
Bene, allora passo successivo, e un'immagine vale
più di mille parole.
Devo escludere campi e relativi span non utilizzati.
Per capirci questo: 
http://tinypic.com/r/14xp1td/6
 
Produce questo: 
http://tinypic.com/r/5vzuhe/6
 
(poi analizzeremo il quarto e ultimo problema)
 
Ciao
nixs 
 

Ho dimenticato di inserire la funzione definitiva:
 
	$("a.preview").click(function() {	 	
		var strMath = ""; 
		var inputValue;
		var spanText;
		$("input[type='text']", $(this).next()).each(function(){
			inputValue = $(this).val();
			spanText = $(this).next().text();
			strMath = strMath + "&#96;" + inputValue + "&#96;" + spanText;
		});
		avviso(strMath);
	});
 
 
 Ciao
 nixs 
 

 less error-prone. 
 
ok, many thanks
 
nixs 

che segue
 
se hai seguito il TD con Andrea vedi che siamo giunti a questa:
 
$("a.preview").click(function() {
      var strMath = "";
      var inputValue;
      var spanText;
      $("input[type='text']", $(this).next()).each(function(){
            inputValue = $(this).val();
            spanText = $(this).next().text();
            strMath = strMath + "&#96;" + inputValue + "&#96;" + spanText;
      });
      avviso(strMath);
});
 
Va bene?


 Va bene?
 
Il resto si`, mi pare. :)
 
Bye.
 

Io tendo a non usare next(), in questi casi
 
capito, e allora quella riga diventerebbe?


 capito, e allora quella riga diventerebbe?
 
Potresti usare come dicevamo il nodo genitore che è il panel contenitore 
come contesto di ricerca.
 
Ti ho anche fatto vedere che "next" accetta un selettore css come 
argomento e quindi potresti renderlo un po' più specifico sul fratello 
da cercare.
 
P.S. non ti do consigli per ora sull'altra questione perché sono un po' 
di corsa e non ho letto con abbastanza attenzione il tuo post per poter 
entrare nel problema.
 
 

e quindi potresti renderlo un po' più specifico sul fratello da cercare.
 
questo forse riesco a farlo:
 
$("input[type='text']", $(this).next(".panel-contents")).each(function(){


...oppure così?
 
$("input[type='text']", $(this).closest('.panel')).each(function(){
 
funzionano entrambi!


 funzionano entrambi!
 
Mi sfugge: se hai due codici che ti funzionano entrambi, cos'è che ti
serve sapere in questo thread?
 
Comunque un approccio alternativo potrebbe essere il seguente: data la
struttura che hai detto di aver seguito, hai l'header che precede il
fieldset, e l'evento click sull'header. Quindi per accedere agli
elementi <input/> (se ho capito, solo quelli di tipo testo) puoi fare anche:
 
    $(this) // h2
        .next() // fieldset
        .find("input[type=text]")
 
A questo punto puoi o iterarli con `each` oppure come fossero un array.
Ovvero:
 
    var inputs = $(this).next().find("input[type=text]")
 
    for (var i = 0, l = inputs.length; i < l, i++) {
        // fai qualcosa con `inputs[i]`
    }
 
Oppure:
 
    $(this).next().find("input[type=text]").each(function(i, input) {
        // fai qualcosa con `input`
    }
 
Come ti è stato detto, se puoi anche passare un selettore a `next` per
avere un margine di sicurezza in più:
 
    $(this).next("fieldset").find("input[type=text]")
 
Vedi te. Io onestamente se ho stabilito un certo tipo di "contratto" (e
sono l'unico che mette mano al codice, ovviamente), solitamente non lo
faccio.

serve sapere in questo thread?
 
La funzione di cattura è a posto, nel senso che riesco a rilevare i campi, 
ciò
che chiedevo era nella catena di messaggi sopra, esclusivamente con Andrea,
Te lo riporto sintetizzando di seguito:
--------------
Spiego il problema.
I miei pannelli contengono campi testo per costruire equazioni, proporzioni
ed equazioni di reazione chimiche. Siccome lo scopo dell'applicazione è
di creare dei test di verifica, la mia idea è di scrivere nei campi gli
elementi di una proporzione (ad esempio) e nascondere l'elemento che si 
chiede
di individuare. Quindi la mia struttura, in questo caso, prevede il pannello
fatto
così:
<td><input><span> : </span></td>
<td><input><span> = </span></td>
<td><input><span> : </span></td>
<td><input></td>
 
Quello che abbiamo appena risolto è il prelievo di quanto digitato nei campi
per creare un'anteprima formattata con MathJax, usando questa funzione:
 
$("a.preview").click(function() {
    var strMath = "";
    var inputValue;
    var spanText;
    $("input[type='text']", $(this).next()).each(function(){
        inputValue = $(this).val();
        spanText = $(this).next().text();
        strMath = strMath + "&#96;" + inputValue + "&#96;" + spanText;
    });
    avviso(strMath); //apre la popup con le stringhe formattate
});
 
Bene, allora passo successivo, e un'immagine vale
più di mille parole.
Devo escludere campi e relativi span non utilizzati.
 
Per capirci questo: 
http://tinypic.com/r/14xp1td/6
 
Produce questo: 
http://tinypic.com/r/5vzuhe/6
 
 
Cioè vengono visualizzati anche i segni
che non dovrebbero essere riportati perchè non sono
stati utilizzati i campi testo relativi.
 
 
(poi analizzeremo il quarto e ultimo problema)
 
 


 stati utilizzati i campi testo relativi.
 
E qui non capisco bene la situazione, perché manca la tua analisi del 
problema e mancano anche tuoi tentativi di soluzione.
 
Un paio di cose a colpo d'occhio.
 
È possibile che alcuni campi non vengano compilati, ma non conosco il 
ragionamento a monte.
Più soluzioni possibili quindi non è necessario compilarli tutti?
Come determini il numero di campi che devono essere lasciati a disposizione?
Quale è il criterio per cui i campi possono essere vuoti?
 
I campi testo non sono relativi a un solo operatore: negli esempi che 
hai riportato gli operatori sono tutti binari e richiedono quindi due 
operandi (campi).
Questo porta alla domanda: quali saranno gli operatori? Che tipologia di 
formule, equazioni o altro abbiamo?
 
Qualche esempio sciocco per spiegarmi.
 
Se abbiamo qualcosa in questa forma:
 
   a + b + c + d = e
 
Vengono compilati, ad esempio, "a" e "d" e tu praticamente dici: 
"elimino gli operatori di somma consecutivi sostituendoli con uno solo".
 
Ma se viene compilato solo "a"?
Applicando lo stesso ragionamento di cui sopra dovrei poi fare una 
seconda analisi per eliminare gli operatori orfani di un operando.
 
Ma se hai:
 
   a + b - c + d = e
 
Non puoi ovviamente applicare il ragionamento precedente.
 
Sono stato forse inutilmente prolisso, ma il punto è: senza la tua 
analisi posso solo aiutarti a determinare quali sono i campi vuoti.

Ma se viene compilato solo "a"?
 
L'esempio che hai fatto è proprio il caso in cui ciò che cerco di fare quasi
non mi serve.
 
Facciamo l'esempio delle reazioni chimiche che è più chiaro.
Io inserisco una reazione usando i campi:
Completare reazioni:
[campo] + [campo] + [campo] + [campo] --> [campo] + [campo] + [campo] + 
[campo]
 
Inserendo:
[vuoto] + [vuoto] + [ 2H ] + [ O2 ] --> [ 2H2O ] + [campo] + [campo] + 
[campo]
 
Poi nascondo (seleziono il RADIO) il campo [ O2 ]
 
Quando faccio l'anteprima (per vedere se mi formatta bene le formule)
non deve considerare i campi vuoti e i segni interposti.
Cioè, adesso, con questa equazione, se faccio l'anteprima mi visualizza:
 
+ + 2H + O2 --> 2H2O + + +
 
Invece dovrebbe visualizzare:
 
2H + O2 --> 2H2O
 
Credo sia chiaro, adesso.


 2H + O2 --> 2H2O
 
Benissimo, ma uno potrebbe anche trovarsi una cosa compilata così:
 
   [ 2H ] + [vuoto] + [ O2 ] + [vuoto] --> [ 2H2O ] ... ecc. ...
 
E qui non basterebbe eliminare gli operatori consecutivi, perché avremmo 
comunque un operatore orfano di un operando dopo "O2".
 
(sempre senza contare le porcherie che potrebbero essere scritte nei campi)
 
Tu stai ponendo tutta l'attenzione all'anteprima di MathJax: devo farlo 
anche io? Facciamo cioè finta che i docenti siano sufficientemente 
disciplinati da compilare tutto come vuoi?
 
Se partiamo da questo assunto e dagli esempi che hai fatto pare che il 
problema sia solo nelle reazioni chimiche, giusto?
Se sì, l'unico problema sembra essere dato da operatori di addizione 
consecutivi nella stringa risultante.
 
Se tutto questo fosse vero basterebbe un poco di "potatura" in questa 
stringa prima di mandarla in pasto a MathJax.
 
Ma se il problema fosse effettivamente solo questo: cosa ti ha bloccato? 
Che tentativi hai fatto?
Sono io ad averlo banalizzato con tutte le supposizioni?

Sono io ad averlo banalizzato con tutte le supposizioni?
 
Mi ha bloccato che non sono capace di "ragionare" con la mente
(destrutturata) di JQuery. Sono abituato ai linguaggi...
conseguenziali e serializzati come VB :-))
Per quanto possa andare a pescare i metodi e le applicazioni delle
funzioni JS e JQ non sono capace di "costruire" un algoritmo
così abbastanza complesso.



 
Provo a scomportela un pelo per farti capire che abbiamo fatto.
 
Gli slash sono i delimitatori dell'espressione regolare, un modo in 
JavaScript per usarla come valore letterale.
 
Il flag "g" dopo l'ultimo slash indica di applicare il "pattern 
matching" all'intera stringa e di non fermarsi quindi alla prima occorrenza.
 
Le parentesi quadre indicano un gruppo di caratteri e l'accento 
circonflesso indica una negazione del suddetto gruppo di caratteri.
 
Quindi questo pattern:
 
   [^ \+]
 
Indica che non vogliamo spazi o segni di addizione; il "+" è preceduto 
da un backslash (carattere di escape) in quanto è un carattere riservato 
nelle espressioni regolari. Facendone l'escape indichiamo che vogliamo 
proprio quel carattere.
 
L'ulteriore "+" che segue le parentesi quadre indica che vogliamo il 
pattern precedente una o più volte.
 
Il metodo "match" di String confronta la stringa con l'espressione 
regolare e restituisce i risultati (match) in un array.
 
Nel nostro caso gli abbiamo detto essenzialmente di ignorare spazi e 
segni di addizione, quindi restituirà un array così fatto:
 
   ["2H", "O2"]
 
Il join finale ci permette di ottenere la stringa che volevamo.
 
Ecco, questa è una soluzione a un problema specifico, poco generica ma 
molto veloce e pratica.
 
Andrà probabilmente cambiata, perché non so come scrivi gli ioni con 
MathJax, ma potrebbe essere per te un esercizio sulle espressioni regolari.
 
Questo è solo uno spunto di partenza, perché, ripeto, i problemi credo 
siano altri: tu comincia a giocarci che magari diventa più precisa anche 
la tua analisi e vedrai se è necessario un altro approccio.
 
P.S. L'espressione regolare l'ho scritta di getto escludendo dei 
caratteri, ma ovviamente nulla vieta di fare il match di ciò che 
vogliamo invece.

tua analisi e vedrai se è necessario un altro approccio.
 
Come inizio non è male... anzi!
a proposito, ora la funzione è diventata (ho dovuto inserire la "-->" tra i 
due membri):
 
$("a.preview").click(function() {
  var counter = 1
  var strMath = "";
  var inputValue;
  var spanText;
  $("input[type='text']", $(this).closest('.panel')).each(function(){
       inputValue = $(this).val();    //il contenuto dei campi input text
       spanText = $(this).next().text();    //i segni nei tag span
       strMath = strMath + inputValue + spanText;
       if (counter == 4){
            strMath = strMath + "->";    //se siamo al quarto campo aggiungi 
la freccia
       }
       counter++;
  });
  strMath = strMath.match(/[^ \+]+/g).join(" + ");
  avviso("#" + strMath + "#");
});


 });
 
Questioni di leggibilità, concisione e lana caprina:
 
- non sei costretto a ripetere "var" per dichiarare una sequenza di 
variabili perché puoi separare le dichiarazioni con una virgola:
 
   var pippo = "cane che guida un'auto",
       pluto = "cane che rosicchia ossi",
       topolino = "ratto che li gestisce";
 
- non mi piace affidarmi a un "counter" in questi frangenti, ma magari è 
la situazione più semplice nel tuo caso. A parte questo posso 
scommettere senza guardare la documentazione di jQuery che di dichiarare 
e incrementare quella variabile non hai proprio bisogno.
 
- ricorda che esiste l'operatore "+=":
 
   strMath += "->";
 
L'ultima questione è invece importante.
 
Nello scope dell'event handler dichiari due variabili "inputValue" e 
"spanText" che poi utilizzi solamente in un altro scope.
Non so se ti è più chiaro, ma è come se dichiarassi due variabili 
globali che poi utilizzi solo dentro una funzione.
Infici la leggibilità, "sporchi" uno degli scope e mostri il fianco a 
bug difficili da scovare.


 
A parte la questione di manutenibilità non mi trovo d'accordo, ma io ne 
faccio esclusivamente una questione di leggibilità, e questa è una cosa 
spesso personale e altrettanto spesso soggetta a cambiamenti di rotta, 
anche drastici, nel tempo.
 
Il passo indietro in manutenibilità è, ai miei occhi, quasi 
insignificante rispetto a quello che accade alla leggibilità per 
giustificare un cambio di rotta.
 
Proprio per motivi di leggibilità uso quella sintassi se le 
dichiarazioni sono talmente semplici / brevi da poter stare su una riga 
e la evito in genere se ci sono di mezzo object literals o array.
 
Magari cambierò idea, magari un giorno deciderò di mettere uno spazio 
dopo gli statements che introducono un blocco di codice e prima della 
graffa di apertura dello stesso.
 
Sono piuttosto sicuro invece che adotterò i "blocchi impuri" (si 
chiamano così?) solo se imposti dalle linee guida di un team o con 
pistola alla tempia.
 
P.S. In verità in altri linguaggi, non so perché, tendo a usare i 
blocchi impuri per nelle dichiarazioni di classi o interfacce e i puri 
per tutto il resto.

difficili da scovare.
 
questa non l'ho capita.
Forse dici di dichiararle dopo (all'interno):
$("input[type='text']", $(this).closest('.panel')).each(function(i){
???
 
Se si, ma in questo modo non vengono dichiarate ad ogni ciclo?


notato come "var" multipli sono più indicati per questione di
manutenibilità e sono meno soggetti ad errori.
 
Anche questo mi sembra un ottimo motivo:
"I am using Single type because of its short and tidy.
However, when debugging in Firebug or Chrome Console, it can not been set 
breakpoints on every variable as it is not a statement.
In Multiple mode, it is easy for debugging every single variable with 
breakpoints."


 faccio esclusivamente una questione di leggibilità, 
 
È proprio per una questione di leggibilità, e quell'articolo espone
chiaramente alcuni punti importanti. Ad esempio, una buona leggibilità è
difficile ottenerla usando un singolo `var`, mentre è molto più semplice
usando `var` multipli, vedasi nel suddetto link la parte relativa a
"Indentation", "More indentation: Multi-line expressions" e "Even more
indentation: Comments".
 
Personalmente non vedo alcuno vantaggio a usare un singolo `var`
rispetto a multipli. Come ho detto, anche io ho iniziato usando il primo
stile per poi passare al secondo.
 
Posso al più capire che, fintanto che si lavora unicamente al proprio
codice non condiviso, uno preferisca rimanere allo stile a cui è abituato.
 
Ma non mi sembra, comunque, una pratica da suggerire di per sé per
"buona leggibilità", visto che non ci sono motivazioni effettive che ne
aumentino obiettivamente la facilità di lettura del codice.

 Se si, ma in questo modo non vengono dichiarate ad ogni ciclo?
 
Verrebbero dichiarate all'interno della funzione, che viene eseguita sì 
a ogni ciclo, ma sono al loro posto visto che è lì che vengono utilizzate.
 
Vedo di inventare un esempio per farmi capire.
 
Ho una lista di nodi "nl" e se la classe del loro genitore è "foo" 
voglio clonarne la struttura e farci qualcosa.
 
   var myParent;
 
   for(var i = 0, l = nl.length; i < l; i++){
       myParent = nl[i].parentNode;
       if(myParent.className == "foo")
           doSomethingWithClone( myParent.cloneNode(true) );
   }
 
E fin qui siam d'accordo: devo usare il genitore più volte, meglio 
appoggiarsi a una variabile. Inutile spreco dichiararla a ogni ciclo, 
quindi la dichiaro fuori.
 
La funzione "doSomethingWithClone" al suo interno però dichiarerà delle 
variabili e viene eseguita a ogni ciclo, ma qui probabilmente non ti 
sarebbe sembrato strano.
 
Hai invece giustamente sfruttato l'accesso allo scope dell'event handler 
per la tua variabile "strMath".
 
Nel tuo caso specifico, peraltro, io non avrei neanche usato le 
variabili d'appoggio:
 
   strMath += $(this).val() + $(this).next().text();
 

strMath += $(this).val() + $(this).next().text();
 
L'ho fatto per MIA chiarezza, così quando riguardo il codice capisco meglio
cosa fa in quel punto.


 sull'eleganza.
 
Vedi?
 
Un modo l'hai trovato, l'eleganza verrà poi.
Ora che hai un qualcosa di funzionante per le reazioni, io ti 
consiglierei di accantonare momentaneamente la casistica degli ioni e 
cominciare a pensare a come vuoi implementare le equazioni.
 
Così dovresti avere tutti i problemi sul tavolo e potrai passare a 
scrivere il codice definitivo.
 

pensare a come vuoi implementare le equazioni.
 
ok, ci provo, anche se la mia logica è troppo inquinata dall'uso ventennale
del basic con il suo ragionare a botte di if e then.
 
Studiando la cosa sono arrivato alla conclusione che nel caso delle
proporzioni e in quello delle equazioni, la soluzione del problema
sta nel non far operare nessuna sostituzione.
Infatti, nel caso delle proporzioni deve inserire pari pari i segni : = :
che trova negli SPAN (perchè si devono usare tutti e quattro i campi).
Nel caso delle equazioni l'unico segno che rileva è il = centrale e
lo deve per forza inserire (tutti gli altri operatori li inserirà l'utente
nei campi).
Quindi:
 
$("a.preview").click(function() {
 
    [...]
 
    //mi vado a leggere il nome del LEGEND che ingloba i campi del pannello, 
che può essere
    // "Proporzione" o "Equazione" o "Reazione" oppure "Reazione"
     var legendName = $("legend", $(this).closest('.panel')).text();
 
      $("input[type='text']", $(this).closest('.panel')).each(function(i){
           [...]
            //Se siamo in uno dei due pannelli "Reazione" e siamo arrivati
            // al 4 campo inserisci la freccia
           if ((legendName == "Reazione") && (i == 3)){
                    strMath += "->";
           }
      });
 
      //se siamo in uno dei due pannelli "Reazione" allora opera le 
sostituzioni
      if (legendName == "Reazione"){
               strMath = strMath.match(/[^ \+]+/g).join(" + ");
               strMath = strMath.replace("+ ->", "->");
               strMath = strMath.replace("-> +", "->");
      }
preview("#" + strMath + "#");
});
 
A funzionare funziona!
Certo, forse non è bella da vedere.
Che ne dici?


 Che ne dici?
 
Dico: bene. Sei arrivato al risultato.
 
Quando avrai finito avrai tutto il tempo per pensare a strade 
alternative, rendere più elegante qualcosa, ecc.
 
Unica nota: io non mi affiderei al valore di una "label" per delle 
condizioni.
I testi sono troppo spesso soggetti a cambiamenti.
 

I testi sono troppo spesso soggetti a cambiamenti.
 
ok, farò tesoro e quando conoscerò meglio js e jQ cercherò un'altra 
soluzione,
intanto ringrazio te e ZERO per la pazienza e competenza, credo che mi farò 
vivo spesso :-))


 farò vivo spesso :-))
 
Grazie a te e a presto!