Salut,

voilà, dans un simple script je gère un click sur un div mais le problème est que si je clique sur un élément à l'intérieur de ce div et bien le click est pris en compte :(

voici un script pour expliquer ... j'aimerais que le click soit pris en compte lorsque je click sur le fond grisé mais pas losque je clique sur le cadre rouge ... de plus, si je clique sur le bouton, ça me lance un click sur le bouton ET le fond grisé

j'ai bien trouvé des trucs mais ils utilisent tous jQuery et je veux faire sans, et ajouter un eventListener n'est pas ce que je cherche ... (ouai je suis chiant) ...

Si quelqu'un a quelques lumière à me proposer je lui serais bien reconnaissant :-) .... merci

voici le code exemple :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>gestion click</title>
    <style>
        #bgModal{
            display: block;
            position: absolute;
            width:100%;
            height:100%;
            background: rgba(0,0,0,0.7);
        }
        #modal{
            width:320px;
            height:320px;
            background: red;
        }
    </style>
</head>
<body>
    <div id="bgModal" onclick="maFonction('le fond grisé')">
        <div id="modal">
            contenu du modal<br>
            <button onclick="maFonction('le bouton')">bouton</button>
        </div>
    </div>

    <script>
        function maFonction(element) {
            console.log('tu as cliqué sur : ',element) ;
        }
    </script>
</body>
</html>

4 réponses


mxmaxime
Réponse acceptée

Salut,
C'est dû au "bubbling". Si tu ne connais pas le concept de capture/bubbling dit le moi je t'expliquerai (je ne connais pas ton niveau en js).
Si tu veux te rafraîchir la mémoire source du w3c ici.

Tu as plusieurs solutions au problème.
Je me base sur ce code html pour les exemples :

  <div id="parent">
    <div id="children">
    </div>
  </div>

Solution : arrêter le bubbling de l'enfant :

const parent = document.querySelector('#parent')
const child = parent.querySelector('#children')
parent.addEventListener('click', function (e) {
    console.log('Parent clicked !')
    })

child.addEventListener('click', (e) => e.stopPropagation())

Solution : vérifier l'event target

const parent = document.querySelector('#parent')
    parent.addEventListener('click', function (e) {
    if (e.target !== parent) return
    console.log('Parent clicked !')
})
mxmaxime
Réponse acceptée

(Re) Bonjour,
Excuses-moi pour l'attente ;-)
"Je sais qu'il existe déjà des bibliothèques plus ou moins poussées pour ça, mais j'aime bricoler pour comprendre comment ça marche ;-)"
Excellent ! C'est comme ça qu'on apprend.

Le onclick ect c'est l'équivalent des addEventListener, c'est juste que c'est déconseillé au profit des eventListeners ;)

Là en gros tu te retrouves avec les fonctions show et hide dans le scope global. Je te conseille plutôt d'utiliser une factory dans ce cas.
Une factory c'est un bien grand mot pour désigner quelque chose de très très simple :
C'est une fonction qui retourne un objet.
Dans ton cas :

const Modal = function () {
    return {
        hide () {}, // = hide: function () {}
        show () {}
    }
}
const modal = Modal()

Ainsi tu as un objet modal qui contient tes fonctions.
Le gros problème avec javascript c'est que tu as 1 milliards de méthodes différente pour arriver au même but, mais les factory c'est très utilisé (et simple).

bahcmoa
Auteur

Merci pour ta réponse Emix. A ce que je vois je n'ai d'autre choix que d'utiliser des eventListener. Le diagramme de ton lien est bien explicite et j'ai bien compris le système, merci. Quant à mon niveau, je le considère comme moyen même si je progresse (doucement).
En fait je travaille sur une petite bibliothèque perso pour créer des boites de dialogue et donc je voulais vraiment utiliser des "onclick" dans le HTML afin de jouer dans le code de la page avec des fonctions "show()" et "hide()" ... mais je vais m'adapter ;-)
Je sais qu'il existe déjà des bibliothèques plus ou moins poussées pour ça, mais j'aime bricoler pour comprendre comment ça marche ;-)

A la rigueur, j'aurais besoin d'une info (rien à voir avec le sujet) ... j'utilise le concepte de la fonction auto appelée pour construire une variable global "modal" afin d'y attacher les deux fonction "show()" et "hide()". le code :

(function(){
    /**
     * blabla de code interne
     */
    // les fonctions
    return modal = {
        // affiche le modal sélectionné -- appel : onClick="modal.show('#id_div_modal')"
        show : function(element){
            // blabla code
        },
        // cache le modal sélectionné -- appel : onClick="modal.hide('#id_div_modal')"
        hide : function(element){
            // blabla code
        }
    } ;

})() ;

si à la place du "return modal" je met une simple déclaration "let modal" les fonctions sont inaccessibles (encapsulage ES6) et si je ne met rien, les fonctions sont accessibles mais là, "modal" n'est pas "déclaré" de manière correcte non ? ... quel serait la bonne syntaxe à ton avis ?
merci :)

bahcmoa
Auteur

Ah oui ! effectivement !!! ... ça m'a pris quelques minutes pour que ma logique comprenne le truc mais oui, c'est vraiment plus pratique ... comme cela je peux avoir autant d'objets auxquels je peux affecter des propriétés différentes au moment de la construction ...

let Modal = function (background) {
    return {
        CouleurBg : background ,
        hide () {}, // = hide: function () {}
        show () {}
    }
};
let modalGris = Modal('grey') ;
let modalBlanc = Modal('white') ;

très intéressant .... un grand merci à toi Emix :-)

Pour ce qui est des "onClick" ... j'avoue que je les préfère car je peux facilement les situer dans le HTML et donc voir à quel élément ils se rattachent alors que les eventListener sont "perdus" au milieu du javascript ... mais bon, c'est une apréciation perso ;-)