JavaScript est-il simple? Sinon, comment puis-je obtenir un accès synchronisé aux données partagées?

J'ai une page Web avec DIV s avec un gestionnaire de passe-passe qui est destiné à afficher une bulle d'information pop-up. Je ne veux pas que plus d'une bulle d'information soit visible à la fois. Mais lorsque l'utilisateur déplace la souris rapidement sur deux éléments, j'ai parfois deux bulles. Cela ne devrait pas se produire, car le code pour afficher un pop-up annule la fenêtre contextuelle précédente.

S'il s'agissait d'un système multi-thread, le problème serait évident: il y a deux threads qui tentent de montrer une fenêtre contextuelle, puis ils annulent les fenêtres contextuelles existantes puis affichent leurs propres fenêtres contextuelles. Mais j'ai supposé que JavaScript est toujours exécuté à simple thread, ce qui empêcherait cela. Ai-je tort? Les gestionnaires d'événements sont-ils exécutés de manière asynchrone, auquel cas j'ai-t-il besoin d'un accès synchronisé aux données partagées ou dois-je rechercher des bugs dans le code de la bibliothèque pour annuler les fenêtres contextuelles?

Modifié pour ajouter:

  • La bibliothèque en question est SIMILE Timeline et sa bibliothèque Ajax;
  • Le gestionnaire d'événements appelle SimileAjax.DOM.cancelEvent(domEvt) , que je suppose en fonction du nom, annule le dérangement des événements;
  • Juste pour rendre les choses plus compliquées, ce que je fais réellement, c'est de démarrer un délai qui, s'il n'est pas annulé par un moustout montre le pop-up, ce qui est destiné à empêcher les fenêtres clignotantes d'agacement, mais gênant d'avoir l'effet inverse.

J'aurai un autre coup d'œil et je verrai si je pouvais savoir où je me trompe. 🙂

Oui, Javascript est simple. Même avec les navigateurs comme Google Chrome, il existe un thread par onglet.

Sans savoir comment vous essayez d'annuler un pop-up d'un autre, il est difficile de dire quelle est la cause de votre problème.

Si vos DIV sont imbriqués l'un dans l'autre, vous pouvez avoir un problème de propagation d'événement .

Je ne connais pas la bibliothèque que vous utilisez, mais si vous essayez seulement d'afficher une info-bulle de somesort à la fois … utilisez un objet flyweight. Fondamentalement, un poids mouche est quelque chose qui est fabriqué une fois et utilisé encore et encore. Pensez à une classe singleton. Donc, vous appelez une classe de manière statique que lorsque la première fois invoqué crée automatiquement un objet de lui-même et l'enregistre. L'une d'elles se produit chaque statique fait référence au même objet et, à cause de cela, vous n'obtenez pas plusieurs info-bulles ou des conflits.

J'utilise ExtJS et ils font des info-bulles, et les boîtes de message comme deux éléments de poids mouche. J'espère que vos cadres ont également des éléments de poids mouche, sinon vous devrez simplement faire votre propre singleton et l'appeler.

Il est simple dans les navigateurs. Les gestionnaires d'événements s'exécutent de manière asynchrone dans un seul thread, le non-blocage ne signifie pas toujours multithread. L'un de vos divs est-il un enfant de l'autre? Parce que les événements se propagent comme des bulles dans l'arbre dom d'un enfant à l'autre.

À l'instar de ce que disait Pkaeding, il est difficile de deviner le problème sans voir votre balisage et votre script. Cependant, j'oserais dire que vous n'arrêtez pas correctement la propagation de l'événement et que vous ne cachez pas correctement l'élément existant. Je ne sais pas si vous utilisez un cadre ou non, mais voici une solution possible en utilisant Prototype:

 // maintain a reference to the active div bubble this.oActiveDivBubble = null; // event handler for the first div $('exampleDiv1').observe('mouseover', function(evt) { evt.stop(); if(this.oActiveDivBubble ) { this.oActiveDivBubble .hide(); } this.oActiveDivBubble = $('exampleDiv1Bubble'); this.oActiveDivBubble .show(); }.bind(this)); // event handler for the second div $('exampleDiv2').observe('mouseover'), function(evt) { evt.stop(); if(this.oActiveDivBubble) { this.oActiveDivBubble.hide(); } this.oActiveDivBubble = $('exampleDiv2Bubble'); this.oActiveDivBubble .show(); }.bind(this)); 

Bien sûr, cela pourrait être généralisé davantage en obtenant tous les éléments avec, par exemple, la même classe, en itérant à travers eux et en appliquant la même fonction de gestion d'événement à chacun d'eux.

Quoi qu'il en soit, j'espère que ça aide.

FYI: à partir de Firefox 3, il y a un changement assez pertinent pour cette discussion: les threads d'exécution provoquant des requêtes synchrones XMLHttpRequest se détachent (c'est pourquoi l'interface ne gèle pas là pendant les demandes synchrones) et l'exécution se poursuit. À la fin de la demande synchrone, son fil continue également. Ils ne seront pas exécutés en même temps, mais en s'appuyant sur l'hypothèse qu'un thread unique s'arrête pendant qu'une procédure synchrone (demande) ne se répercute plus.

Il se peut que l'affichage ne soit pas rafraîchissant assez rapidement. Selon la bibliothèque JS que vous utilisez, vous pourriez peut-être mettre un petit retard sur l'effet "show".

Voici la version de travail, plus ou moins. Lorsque vous créez des éléments, nous joignons un événement mouseover :

 var self = this; SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mouseover", function (elt, domEvt, target) { return self._onHover(labelElmtData.elmt, domEvt, evt); }); 

Cela appelle une fonction qui définit un délai d'attente (les délais d'attente préexistants pour un élément différent sont annulés en premier):

 MyPlan.EventPainter.prototype._onHover = function(target, domEvt, evt) { ... calculate x and y ... domEvt.cancelBubble = true; SimileAjax.DOM.cancelEvent(domEvt); this._futureShowBubble(x, y, evt); return false; } MyPlan.EventPainter.prototype._futureShowBubble = function (x, y, evt) { if (this._futurePopup) { if (evt.getID() == this._futurePopup.evt.getID()) { return; } else { /* We had queued a different event's pop-up; this must now be cancelled. */ window.clearTimeout(this._futurePopup.timeoutID); } } this._futurePopup = { x: x, y: y, evt: evt }; var self = this; this._futurePopup.timeoutID = window.setTimeout(function () { self._onTimeout(); }, this._popupTimeout); } 

Cela montre à son tour la bulle si elle se déclenche avant d'être annulée:

 MyPlan.EventPainter.prototype._onTimeout = function () { this._showBubble(this._futurePopup.x, this._futurePopup.y, this._futurePopup.evt); }; MyPlan.EventPainter.prototype._showBubble = function(x, y, evt) { if (this._futurePopup) { window.clearTimeout(this._futurePopup.timeoutID); this._futurePopup = null; } ... SimileAjax.WindowManager.cancelPopups(); SimileAjax.Graphics.createBubbleForContentAndPoint(...); }; 

Cela semble fonctionner maintenant, j'ai défini le délai d'attente à 200 ms plutôt que 100 ms. Vous ne savez pas pourquoi un délai d'attente trop court entraîne l'apparition de la chose à plusieurs bulles, mais je suppose que la mise en file d'attente des événements de fenêtre ou quelque chose pourrait se produire alors que les éléments nouvellement ajoutés sont définis.