web-dev-qa-db-fra.com

Comment gérer un clic n'importe où dans la page, même lorsqu'un certain élément arrête la propagation?

Nous travaillons sur un outil JavaScript qui contient du code plus ancien, Nous ne pouvons donc pas réécrire l’ensemble de l’outil.

Maintenant, un menu a été ajouté positionné en bas et le client aimerait beaucoup avoir un bouton à bascule pour ouvrir et fermer le menu, Sauf que la fermeture doit obligatoirement se produire automatiquement lorsqu'un utilisateur commence à faire des choses en dehors du menu, par exemple, lorsqu'un utilisateur revient dans la page et sélectionne un élément ou clique sur un champ de formulaire.

Cela pourrait techniquement fonctionner avec un événement click sur la body, déclenchant n'importe quel clic, Cependant, il existe de nombreux éléments dans l'ancien code, dans lesquels un événement de clic a été géré sur un lien interne, et return false a été ajouté à la fonction de clic, afin que le site ne continue pas à la balise href du lien.

Donc, clairement, une fonction générale comme celle-ci fonctionne, mais pas lorsque vous cliquez sur un lien interne où le retour false arrête la propagation.

$('body').click(function(){
  console.log('clicked');
});

Y a-t-il un moyen de forcer l'événement de clic du corps de toute façon, .__ ou un autre moyen de laisser le menu disparaître, en utilisant un événement de clic global ou quelque chose de similaire?

Sans avoir à réécrire tous les autres clics dans l'application créés il y a des années… .. Ce serait une tâche monstre, d'autant plus que je ne sais pas comment les réécrire, sans le retour faux, mais ne les laissez pas quand même aller à leur href.

32
Sander

Les événements dans les implémentations modernes du DOM ont deux phases, capturing et bubbling. La phase de capture est la première phase, allant de la defaultView du document à la cible de l'événement, suivie de la phase de formation de bulles, passant de la cible de l'événement à la defaultView. Pour plus d'informations, voir http://www.w3.org/TR/DOM-Level-3-Events/#event-flow .

Pour gérer la phase de capture d'un événement, vous devez définir le troisième argument de addEventListener sur true:

document.body.addEventListener('click', fn, true); 

Malheureusement, comme l'a mentionné Wesley, la phase de capture d'un événement ne peut pas être gérée de manière fiable, voire pas du tout, dans les navigateurs plus anciens.

Une solution possible consiste à gérer l'événement mouseup à la place, puisque l'ordre des événements pour les clics est le suivant:

  1. souris vers le bas
  2. mouseup
  3. cliquez sur

Si vous pouvez être sûr que aucun gestionnaire n'annule l'événement mouseup, c'est un moyen (et sans doute un meilleur moyen) d'aller. Une autre chose à noter est que beaucoup, sinon la plupart (sinon la totalité) des menus de l'interface utilisateur disparaissent lorsque la souris est enfoncée.

55
Andy E

En coopération avec Andy E, voici le côté obscur de la force:

var _old = jQuery.Event.prototype.stopPropagation;

jQuery.Event.prototype.stopPropagation = function() {
    this.target.nodeName !== 'SPAN' && _old.apply( this, arguments );
};     

Exemple: http://jsfiddle.net/M4teA/2/

Rappelez-vous que si tous les événements étaient liés via jQuery, vous pouvez gérer ces cas ici. Dans cet exemple, nous appelons simplement la .stopPropagation() originale s'il ne s'agit pas d'un <span>.


Vous ne pouvez pas empêcher la prévention, non.

Ce que vous pouvez faire est de réécrire ces gestionnaires d’événements manuellement dans le code. C'est une affaire délicate, mais si vous savez accéder aux méthodes de gestionnaire stockées, vous pouvez les contourner. J'ai un peu joué avec, et voici mon résultat:

$( document.body ).click(function() {
    alert('Hi I am bound to the body!');
});

$( '#bar' ).click(function(e) {
    alert('I am the span and I do prevent propagation');
    e.stopPropagation();
});

$( '#yay' ).click(function() {
    $('span').each(function(i, elem) {
        var events        = jQuery._data(elem).events,
            oldHandler    = [ ],
            $elem         = $( elem );

        if( 'click' in events ) {                        
            [].forEach.call( events.click, function( click ) {
                oldHandler.Push( click.handler );
            });

            $elem.off( 'click' );
        }

        if( oldHandler.length ) {
            oldHandler.forEach(function( handler ) {
                $elem.bind( 'click', (function( h ) {
                    return function() {
                        h.apply( this, [{stopPropagation: $.noop}] );
                    };
                }( handler )));
            });
        }
    });

    this.disabled = 1;
    return false;
});

Exemple: http://jsfiddle.net/M4teA/

Notez que le code ci-dessus ne fonctionnera qu'avec jQuery 1.7. Si ces événements de clic étaient liés à une version antérieure de jQuery ou "inline", vous pouvez toujours utiliser le code, mais vous devrez accéder à "l'ancien gestionnaire" différemment.

Je sais que je suppose beaucoup de choses du scénario "monde parfait", par exemple, que ces descripteurs appellent explicitement .stopPropagation() au lieu de renvoyer false. Donc cela reste peut-être un exemple académique inutile, mais je me suis senti obligé de le sortir :-)

edit: hé, return false; fonctionnera parfaitement, les objets d'événement sont accessibles de la même manière.

6
jAndy

Si vous vous assurez qu'il s'agit bien du travail du gestionnaire d'événement first , il se peut que quelque chose comme ceci fasse l'affaire:

$('*').click(function(event) {
    if (this === event.target) { // only fire this handler on the original element
        alert('clicked');
    }
});

Notez que si votre page contient de nombreux éléments, ce sera vraiment très lent et cela ne fonctionnera pas pour les éléments ajoutés dynamiquement.

3
lonesomeday

Ce que vous voulez vraiment faire est de lier le gestionnaire d’événements pour la phase de capture de l’événement. Cependant, autant que je sache, cela n’est pas pris en charge dans IE, de sorte que cela pourrait ne pas être très utile.

http://www.quirksmode.org/js/events_order.html

Questions connexes:

2
Wesley Petrowski

Vous pouvez utiliser jQuery pour ajouter un écouteur d'événement sur le document DOM.

    $(document).on("click", function () {
        console.log('clicked');
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

1
Ryan

Je pense que c'est ce dont vous avez besoin:

$("body").trigger("click");

Cela vous permettra de déclencher l'événement click du corps à partir de n'importe où.

0
James Johnson

this est la clé (vs evt.target). Voir exemple.

document.body.addEventListener("click", function (evt) {
    console.dir(this);
    //note evt.target can be a nested element, not the body element, resulting in misfires
    console.log(evt.target);
    alert("body clicked");
});
<h4>This is a heading.</h4>
<p>this is a paragraph.</p>

0
Ron Royston