[Javascript] Stranezza con comparazione OR

piero60

Utente Attivo
26 Gen 2015
71
2
8
Salve a tutti,

conosco poco javascript, sicuramente sbaglio qualcosa ma non capisco cosa.
Ho un form con delle checkbox

<input type="checkbox" id="orari1" name="crea_orari[]" value="08:00">08:00
<input type="checkbox" id="orari2" name="crea_orari[]" value="08:30">08:30
<input type="checkbox" id="orari3" name="crea_orari[]" value="09:00">09:00
ecc.ecc. ne ho parecchi ncora sotto.

Devo controllare che almeno 1 sia stato selezionato.

ho uno script che viene chiamato all'invio del form che effettua i vari controlli
HTML:
<script language="JavaScript" type="text/javascript">

    function controllo() {
        with(document.modulo) {

            if (titolo.value == "") {
                alert("Errore: INSERIRE UN TITOLO");
                titolo.focus();
                return false;
            }       
                                                                                                                    
            if (data_f.value == "") {
                alert("Errore: INSERIRE LA DATA");
                data_f.focus();
                return false;
            }
                                            
            if (document.getElementById("orari1" || "orari2" || "orari3").checked)
            {
                return true;
            } else {
                alert("Errore: SELEZIONARE ALMENO UN ORARIO");
                return false;
  }                                                   
 }
}
</script>
se nella riga:
if (document.getElementById("orari1" || "orari2" || "orari3").checked)

invece di utilizzare OR || che è ciò che a me serve, utilizzo AND && tutto funziona
mi segnala l'errore se non sono selezionati tutti gli elementi comparati.

Se lascio OR || e seleziono il primo valore "orari1" tutto ok, non da errore, ed il form
parte regolarmente.
Se seleziono invece il secondo "orari2" o il terzo "orari3", mi da errore come se nulla fosse selezionato.

Eppure è una semplice OR se uno qualsiasi dei 3 è selezionato dovrebbe essere tutto OK.

Cosa sbaglio?

Grazie.
 

piero60

Utente Attivo
26 Gen 2015
71
2
8
Ho risolto così:

HTML:
 if ((document.modulo.orari1.checked == false) && (document.modulo.orari2.checked == false) && (document.modulo.orari3.checked == false) && (document.modulo.orari4.checked == false)
 && (document.modulo.orari5.checked == false) && (document.modulo.orari6.checked == false) && (document.modulo.orari7.checked == false)  && (document.modulo.orari8.checked == false)
 && (document.modulo.orari9.checked == false) && (document.modulo.orari10.checked == false) && (document.modulo.orari11.checked == false)  && (document.modulo.orari12.checked == false)
 && (document.modulo.orari13.checked == false) && (document.modulo.orari14.checked == false) && (document.modulo.orari15.checked == false)  && (document.modulo.orari16.checked == false)
 && (document.modulo.orari17.checked == false) && (document.modulo.orari18.checked == false) && (document.modulo.orari19.checked == false)  && (document.modulo.orari20.checked == false)
 && (document.modulo.orari21.checked == false) && (document.modulo.orari22.checked == false) && (document.modulo.orari23.checked == false)  && (document.modulo.orari24.checked == false)
 && (document.modulo.orari25.checked == false) && (document.modulo.orari26.checked == false) && (document.modulo.orari27.checked == false)  && (document.modulo.orari28.checked == false)
 && (document.modulo.orari29.checked == false) && (document.modulo.orari30.checked == false) && (document.modulo.orari31.checked == false)  && (document.modulo.orari32.checked == false)
 && (document.modulo.orari33.checked == false) && (document.modulo.orari34.checked == false) && (document.modulo.orari35.checked == false)  && (document.modulo.orari36.checked == false)
 && (document.modulo.orari37.checked == false) && (document.modulo.orari38.checked == false) && (document.modulo.orari39.checked == false)  && (document.modulo.orari40.checked == false)
 && (document.modulo.orari41.checked == false) && (document.modulo.orari42.checked == false))
 {
  alert("SELEZIONARE ALMENO UN ORARIO DI PRENOTAZIONE");
  document.modulo.orari1.focus();
  return false;
  }
Funziona ma vorrei capire perchè con il sistema precedente gli AND funzionano e gli OR invece no.
 

WmbertSea

Utente Attivo
28 Nov 2014
178
27
28
Ciao, ci sono dei problemi di fondo nello script che cercavi di utilizzare.
Quella che tu definisci "stranezza" in realtà è il normale comportamento. Come tu stesso dici, la tua conoscenza limitata di JavaScript ti ha portato sicuramente ad avere confusione sul funzionamento di tale script e sull'uso dei metodi utilizzati.

Per una serie di coincidenze hai anche ottenuto alcuni risultati "plausibili" ma con una logica sbagliata, i quali non sarebbero comunque da prendere in considerazione.
Anche usando &&, come hai detto di aver fatto, otterresti una condizione "vera" se l'ultimo checkbox risulta spuntato, anche quando gli altri non lo sono. Ovviamente spuntandoli tutti hai ottenuto comunque un risultato che sembrerebbe realistico, dovuto però al solo fatto che quell'ultimo checkbox è spuntato. Si tratta di una coincidenza.

Ora ti spiego come funziona e dove hai sbagliato.

Partiamo dal fatto che il metodo getElementById() accetta un argomento che necessariamente deve essere di tipo "String" e che deve far riferimento ad un valore id di un elemento presente nel DOM. Ovviamente non ha molto senso passargli un'espressione con degli operatori logici come hai fatto tu.

Cosa avviene quindi con quella tua espressione?
Questa viene chiaramente valutata da subito e il risultato viene poi passato al metodo in questione.

Con qualche semplice log puoi verificare cosa viene restituito dalle due espressioni:
Codice:
console.log("orari1" || "orari2" || "orari3") // => "orari1"
Codice:
console.log("orari1" && "orari2" && "orari3") // => "orari3"
Nota che con gli || (OR) viene restituito il primo valore dell'espressione, mentre con gli && (AND) viene restituito l'ultimo.

Perché avviene questo?
In una espressione logica vengono valutati singolarmente e in modo sequenziale, da sinistra a destra, i vari membri che compongono quell'espressione.
La valutazione si interrompe non appena viene raggiunta la situazione in cui il risultato ottenuto (in quel momento) non può subire ulteriori variazioni (qualora si valutasse anche il resto degli altri membri).

Tra parentesi, JavaScript ha un sistema di auto-casting. Questo significa che il tipo di valore usato può essere convertito automaticamente dall'interprete JavaScript in base al contesto in cui è utilizzato. In un'espressione logica, in cui serve avere dei valori booleani durante la valutazione, qualsiasi stringa non vuota, per JavaScript assume il valore true.
Va da sé quindi che in una sequenza di || (OR) la valutazione si interrompe quando viene incontrato il primo valore che risulta essere true; ragione per cui nella prima espressione il risultato è "orari1". Questo non perché l'elemento orari1 sia spuntato, ma per il semplice motivo che quel valore, in quella espressione, è una stringa non vuota.

Viceversa, la valutazione di una serie di && (AND) si interrompe quando viene incontrato il primo valore che risulta essere false, restituendo quel valore o, se non ci sono valori false, restituendo l'ultimo valore, il quale risulta essere true; ragione per cui nella seconda espressione il risultato è "orari3".

Questo significa che al metodo getElementById() viene passato di fatto quel singolo valore restituito da quell'espressione.

Quindi nel tuo caso, con la prima espressione, è come se avessi scritto:
Codice:
document.getElementById("orari1").checked
Mentre, con la seconda espressione, è come se avessi scritto:
Codice:
document.getElementById("orari3").checked
Questo determina il risultato della tua condizione if.
Va da sé che con quegli || (OR) ti funziona solo il caso in cui almeno "orari1" è spuntato (a prescindere dal fatto che gli altri due siano spuntati o meno) proprio perché nella condizione stai valutando la proprietà checked di quello specifico elemento; mentre con gli && (AND) funziona solo quando almeno "orari3" è spuntato (anche qui, a prescindere dal fatto che gli altri due siano spuntati o meno).

In teoria, con l'uso del metodo getElementById(), avresti dovuto fare in questo modo (analogamente a come hai poi fatto senza quel metodo):
Codice:
// Con || (OR)
if (  document.getElementById("orari1").checked
  || document.getElementById("orari2").checked
  || document.getElementById("orari3").checked ){
  console.log("Almeno uno dei checkbox è spuntato");
}
// Con && (AND)
if ( document.getElementById("orari1").checked
  && document.getElementById("orari2").checked
  && document.getElementById("orari3").checked ){
  console.log("Tutti i checkbox sono spuntati");
}
Chiaramente, nonostante la sintassi e la logica siano giuste, questo script risulta essere ridondante, analogamente a quello che hai fatto tu dopo.

Si possono però usare sistemi differenti per snellire il tutto.

Uno di questi può essere l'uso del metodo querySelectorAll() che ti permette di selezionare gli elementi attraverso un selettore CSS.

Supponendo che i checkbox siano contenuti dentro un elemento con id="modulo", potresti quindi risolvere in una maniera del genere

Codice:
if (document.querySelectorAll("#modulo input[id^=orari]:checked").length == 0){
  alert("SELEZIONARE ALMENO UN ORARIO DI PRENOTAZIONE");
  // ...
}
Ti spiego in breve (ci vorrebbe un'altra discussione per spiegare nel dettaglio):

  • Il metodo querySelectorAll seleziona gli elementi del DOM secondo il selettore specificato e restituisce una collection (nello specifico un oggetto NodeList, in sostanza un insieme) degli elementi trovati.
  • La proprietà length (in un oggetto NodeList) rappresenta il numero di elementi trovati, oppure avrà valore 0 se non viene trovato alcun elemento,
  • La stringa "#modulo input[id^=orari]:checked" è un selettore CSS (in questo caso specifico si tratta di CSS3). In sostanza vengono selezionati tutti gli input dentro #modulo, il cui id inizia con "orari" e che risultano spuntati.

Ovviamente la costruzione del selettore dipende poi dalla reale situazione che hai sul codice HTML. Magari l'uso di una classe, per distinguere quei checkbox, potrebbe andare anche meglio; il selettore potrebbe risultare una cosa tipo ".chk_orari:checked". Ma questo devi vederlo tu.

Sicuramente esistono altri svariati metodi; questo mi sembra quello più conciso e pulito.

Se poi servono ulteriori chiarimenti, sull'oggetto in discussione, puoi chiedere qui.

Buon proseguimento.


Nota a margine:
Giusto un consiglio. Chiaramente non conosco la tua situazione e forse non c'è nemmeno bisogno di dirtelo, ma in questi casi è sempre bene ricordarsi che qualsiasi controllo fai lato client dovresti poi farlo opportunamente anche lato server.
 
Ultima modifica:

piero60

Utente Attivo
26 Gen 2015
71
2
8
Grazie WmbertSea,

ottima ed esauriente spiegazione, come ho detto inizialmente conosco pochissimo Javascript sono più su PHP ma soprattutto ASSEMBLER, linguaggi con approccio totalmente differente da Javascript.
Il controllo su quei campi specifici lo effettuavo già sul lato server, ho voluto aggiungerlo anche nel modulo per evitare in caso di errore di riportarmi indietro tutte le variabili precedentemente compilate nel form.
Solitamente per questioni di sicurezza spezzetto sempre il form da compilare dalla parte che poi effettivamente svolge le conseguenti operazioni.

Buon pomeriggio.