Soit l'un soit aucun

Comment présenter à l'utilisateur une interface graphique où il peut sélectionner un choix parmi plusieurs mais aussi n'en choisir aucun. Deux implémentations possibles, l'une avec des boutons radio l'autre avec des cases à cocher.

Utilisation de deux boutons radio

<p>
    <label for="c1">Choix 1</label>
    <input type="radio" name="choice" value="c1" id="c1">
</p>
<p>
    <label for="c2">Choix 2</label>
    <input type="radio" name="choice" value="c2" id="c2">
</p>
Code HTML
let checked = false;
const c1 = document.getElementById('c1');
const c2 = document.getElementById('c2');
const onMouseDown = (e) => {
    checked = e.target.checked;
};
const onClick = (e) => {
    if (checked) {
        e.target.checked = false;
    }
};
c1.addEventListener('mousedown', onMouseDown);
c1.addEventListener('click', onClick);
c2.addEventListener('mousedown', onMouseDown);
c2.addEventListener('click', onClick);
Code javascript

Utilisation de deux cases à cocher

<p>
    <label for="c1">Choix 1</label>
    <input type="checkbox" name="c1" value="c1" id="c1">
</p>
<p>
    <label for="c2">Choix 2</label>
    <input type="checkbox" name="c2" value="c2" id="c2">
</p>
Code HTML
const c1 = document.getElementById('c1');
const c2 = document.getElementById('c2');
const onClick = (e) => {
    const opp = e.target === c1 ? c2 : c1;
    if (e.target.checked) {
        opp.checked = false;
    }
};
c1.addEventListener('click', onClick);
c2.addEventListener('click', onClick);
Code javascript

Gérer plus de deux choix

On peut adapter le principe à plus de deux choix en faisant cependant attention : si on dépasse trois alternatives, il est sans doute plus pertinent d'utiliser une liste de sélection. On rajoute ici un moyen simple de se mettre à l'écoute des changements et d’interagir de manière programmatique via une API simple.

<p>
    <label for="cb1">Choix 1</label>
    <input type="checkbox" name="cb1" value="cb1" id="cb1">
</p>
<p>
    <label for="cb2">Choix 2</label>
    <input type="checkbox" name="cb2" value="cb2" id="cb2">
</p>
<p>
    <label for="cb3">Choix 3</label>
    <input type="checkbox" name="cb3" value="cb3" id="cb3">
</p>
Code HTML

/**
 * Fired when value changes.
 * @event oneOrNeitherChange
 * @type {CustomEvent}
 */

 /**
  * @fires oneOrNeitherChange When value checked change.
  */
class OneOrNeither extends EventTarget {

    /**
     * @param {HTMLInputElement[]} fields 
     */
    constructor (fields) {
        super();
        /** @private */
        this.current = null;
        /** @private */
        this.fields = fields;
        const listener = (e) => {
            const target = e.target;
            if (target.checked) {
                this.fields.forEach((field) => {
                    if (field !== target) {
                        field.checked = false;
                    }
                });
                this.current = target;
            } else {
                this.current = null;
            }
            this.dispatchEvent(new CustomEvent('oneOrNeitherChange'));
        };
        this.fields.forEach((field) => {
            field.addEventListener('click', listener);
        });
    }

    get value () {
        return this.current ? this.current.value : null;
    }

    set value (value) {
        const c = this.fields.find(field => field.value === value);
        if (c) {
            c.checked = true;
            if (this.current && c !== this.current) {
                this.current.checked = false;
            }
            this.current = c;
        }
    }
}

export default OneOrNeither;
OneOrNeither.js
import OneOrNeither from './OneOrNeither';

const manager = new OneOrNeither([
    document.getElementById('cb1'),
    document.getElementById('cb2'),
    document.getElementById('cb3'),
]);

manager.addEventListener('oneOrNeitherChange', (e) => {
    console.log(e.target.value);
});
Utilisation