Bestellproblem bei onclick () und onblur ()

101

Ich habe ein Eingabefeld, das ein benutzerdefiniertes Dropdown-Menü aufruft. Ich möchte die folgende Funktionalität:

  • Wenn der Benutzer irgendwo außerhalb des Eingabefelds klickt, sollte das Menü entfernt werden.
  • Wenn der Benutzer im Menü auf ein Div klickt, sollte das Menü entfernt werden, und es sollte eine spezielle Verarbeitung erfolgen, die darauf basiert, auf welches Div geklickt wurde.

Hier ist meine Implementierung:

Das Eingabefeld enthält ein onblur()Ereignis, das das Menü löscht (indem die übergeordneten Elemente innerHTMLauf eine leere Zeichenfolge gesetzt werden), wenn der Benutzer außerhalb des Eingabefelds klickt. Die Divs im Menü haben auch onclick()Ereignisse, die die spezielle Verarbeitung ausführen.

Das Problem ist, dass die onclick()Ereignisse niemals ausgelöst werden, wenn auf das Menü geklickt wird, da das Eingabefeld onblur()zuerst ausgelöst wird und das Menü einschließlich des onclick()s!

Ich habe das Problem gelöst, indem ich die Menü-Divs onclick()in onmousedown()und onmouseup()Ereignisse aufgeteilt und ein globales Flag bei gedrückter Maus gesetzt habe, das mit der Maus nach oben gelöscht wird, ähnlich wie in dieser Antwort vorgeschlagen . Da onmousedown()zuvor ausgelöst wurde onblur(), wird das Flag gesetzt, onblur()wenn auf eines der Menü-Divs geklickt wurde, nicht jedoch, wenn es sich an einer anderen Stelle auf dem Bildschirm befand. Wenn auf das Menü geklickt wurde, kehre ich sofort zurück, onblur()ohne das Menü zu löschen, und warte dann, onclick()bis das Menü ausgelöst wird. An diesem Punkt kann ich das Menü sicher löschen.

Gibt es eine elegantere Lösung?

Der Code sieht ungefähr so ​​aus:

<div class="menu" onmousedown="setFlag()" onmouseup="doProcessing()">...</div>
<input id="input" onblur="removeMenu()" ... />

var mouseflag;

function setFlag() {
    mouseflag = true;
}

function removeMenu() {
    if (!mouseflag) {
        document.getElementById('menu').innerHTML = '';
    }
}

function doProcessing(id, name) {
    mouseflag = false;
    ...
}
1 ''
quelle

Antworten:

164

Ich hatte genau das gleiche Problem wie Sie. Meine Benutzeroberfläche ist genau so gestaltet, wie Sie es beschreiben. Ich habe das Problem gelöst, indem ich einfach onClickdie Menüpunkte durch eine ersetzt habe onMouseDown. Ich habe nichts anderes getan; Nein onMouseUp, keine Fahnen. Dies löste das Problem, indem der Browser automatisch nach der Priorität dieser Ereignishandler neu geordnet wurde, ohne dass ich zusätzliche Arbeit leisten musste.

Gibt es einen Grund, warum dies nicht auch für Sie funktioniert hätte?

Johnbakers
quelle
3
Das funktioniert tatsächlich. Ich habe es in FF getestet und es funktioniert wie Charme.
Supreme Dolphin
3
Es klappt. Es sieht so aus, als würde onmousedownes vor dem onblurTesten in Firefox, Chrome und Internet Explorer ausgeführt.
Tonatio
11
Bemerkenswert ist, dass dies das Verhalten ein wenig auf natürliche Weise ändert - die Klick-Interaktion wird dann mit der Maus nach unten und nicht mit der Maus nach oben ausgeführt. Für die meisten Menschen mag das in Ordnung sein (in diesem Fall selbst eingeschlossen), aber es gibt ein paar Nachteile. Vor allem klicke ich oft und ziehe die Schaltfläche ab, wenn ich falsch geklickt habe, wodurch verhindert wird, dass ein Klick aufgerufen wird. Wenn die Schaltfläche eine nicht reine Funktion (Löschen, Posten usw.) ausführt, möchten Sie diese möglicherweise beibehalten und den Flag-Ansatz wählen.
Brizee
2
Was ist mit der Barrierefreiheit in dem Moment, in dem Sie mouseDown anstelle von onClick setzen? Sie beenden die Barrierefreiheit für alle <button> -Elemente: /
ncubica
2
Die unten von @ ian-macfarlane aufgeführte Antwort ist die richtige Art, mit diesem Problem umzugehen. Zusammenfassend ... onMouseDownmit event.preventDefault() und onClickmit Ihrer gewünschten Aktion.
Conal Trinitrotoluol Da Costa
45

onClicksollte nicht durch ersetzt werden onMouseDown.

Während dieser Ansatz etwas funktioniert , handelt es sich bei den beiden Ereignissen um grundlegend unterschiedliche Ereignisse, die in den Augen des Benutzers unterschiedliche Erwartungen haben. Die Verwendung von onMouseDownanstelle von onClickwird in diesem Fall die Vorhersehbarkeit Ihrer Software beeinträchtigen. Somit sind die beiden Ereignisse nicht austauschbar.

Zur Veranschaulichung: Wenn Benutzer versehentlich auf eine Schaltfläche klicken, können sie den Mausklick gedrückt halten, den Cursor außerhalb des Elements ziehen und die Maustaste loslassen, was letztendlich zu keiner Aktion führt. onClickmacht dies. onMouseDownDer Benutzer kann die Maus nicht gedrückt halten und löst stattdessen sofort eine Aktion aus, ohne dass der Benutzer darauf zurückgreifen muss. onClickist der Standard, nach dem wir erwarten, Aktionen auf einem Computer auszulösen.

Rufen Sie in dieser Situation event.preventDefault()die onMouseDownVeranstaltung auf. onMouseDownverursacht standardmäßig ein Unschärfeereignis und tut dies nicht, wenn preventDefaultes aufgerufen wird. Dann onClickhaben Sie die Chance, angerufen zu werden. Ein Unschärfeereignis wird immer noch auftreten, erst danach onClick.

Schließlich ist das onClickEreignis eine Kombination aus onMouseDownund onMouseUp, wenn und nur wenn beide innerhalb desselben Elements auftreten.

Ian MacFarlane
quelle
7
Dies war die perfekte Lösung für mich und der Kommentar ist völlig korrekt - das Ersetzen von onMouseDown ist für die Benutzererfahrung verwirrend. Habe abgestimmt.
Paul Bartlett
5
Dies sollte die richtige Antwort sein. Vielen Dank für die Veröffentlichung
user1189352
1
Es ist erwähnenswert, dass dies auch einige geringfügige Nebenwirkungen hat, z. B. dass Sie keinen Text auswählen können, indem Sie in das Element klicken. Ich kann mir nicht allzu viele Fälle vorstellen, in denen dies ein Problem wäre, nur dass es sich ein bisschen komisch anfühlt.
Rei Miyasaka
1
Wenn ich event.preventDefault()ein onMouseDownEreignis verwende, wird mein onFocusEreignis nicht aufgerufen
Batman
4

Ersetzen onmousedowndurch onfocus. Dieses Ereignis wird also ausgelöst, wenn sich der Fokus innerhalb des Textfelds befindet.

Ersetzen onmouseupdurch onblur. Sobald Sie Ihren Fokus aus dem Textfeld entfernen, wird onblur ausgeführt.

Ich denke, das ist es, was du vielleicht brauchst.

UPDATE :

Wenn Sie Ihre Funktion onfocus ausführen -> entfernen Sie die Klassen, die Sie in onblur anwenden möchten, und fügen Sie die Klassen hinzu, die onfocus ausgeführt werden sollen

und

Wenn Sie Ihre Funktion onblur ausführen -> entfernen Sie die Klassen, die Sie in onfocus anwenden möchten, und fügen Sie die Klassen hinzu, die onblur ausgeführt werden sollen

Ich sehe keine Notwendigkeit für Flag-Variablen.

UPDATE 2:

Sie können die Ereignisse onmouseout und onmouseover verwenden

onmouseover - Erkennt, wenn sich der Cursor darüber befindet.

onmouseout - Erkennt, wann der Cursor geht.

HIRA THAKUR
quelle
Ich habe meine Frage aus Gründen der Klarheit bearbeitet. Wie vermeidet diese Lösung das Setzen eines Flags? Außerdem benutze ich onmousedown()und onmouseup()im Menü nicht das Textfeld / Eingabefeld.
1 ''
Ich kann diese Antwort noch nicht akzeptieren, weil ich nicht weiß, dass sie richtig ist. Könnten Sie auf die Bedenken antworten, die ich in meinem obigen Kommentar geäußert habe?
1
Klar, ich kann sehen, wie Sie onmouseover und onmouseout ähnlich wie derzeit verwenden können, um ein Flag zu setzen, wenn sich die Maus über dem Menü befindet. Ich denke jedoch immer noch, dass Sie in onmouseover ein Flag setzen müssen, das in onmouseout gelöscht wird, damit Sie wissen, ob Sie sich beim Klicken über dem Menü befanden. Können Sie sich einen Weg vorstellen, bei dem keine Flagge gesetzt werden muss?
1
Kann ich sehen, dass Sie Code ... setzen Sie es in eine Geige ... setzen Sie einfach den relevanten Teil und es ist völlig in Ordnung, wenn ich die Ergebnisse nicht sehe ... nur den Code ..
HIRA THAKUR
Lassen Sie uns diese Diskussion im Chat fortsetzen
HIRA THAKUR
2

Eine elegantere (aber wahrscheinlich weniger performante) Lösung:

Anstatt die Onblur der Eingabe zu verwenden, um das Menü zu entfernen, verwenden Sie document.onclick, was danach ausgelöst wird onblur.

Dies bedeutet jedoch auch, dass das Menü entfernt wird, wenn auf die Eingabe selbst geklickt wird. Dies ist ein unerwünschtes Verhalten. Setzen Sie ein input.onclickmit event.stopPropagation(), um zu vermeiden, dass Klicks auf das Dokumentklickereignis übertragen werden.

1 ''
quelle
5
Das Problem mit dieser Antwort ist jedoch, dass es kein Klickereignis war, das die Unschärfe auslöste. Was ist, wenn der Benutzer die Eingabe beendet hat? Dies würde dazu führen, dass das Unschärfeereignis ausgelöst wird, das Flyout-Menü jedoch weiterhin sichtbar ist.
Christoph
0

Ändern Sie onclick durch onfocus

auch wenn onblur und onclick nicht sehr gut miteinander auskommen, aber offensichtlich onfocus und ja onblur. da auch nach dem Schließen des Menüs der Onfocus für das darin angeklickte Element weiterhin gültig ist.

Ich habe es getan und es hat funktioniert.

Brasilien
quelle
-7

Sie können eine setIntervalFunktion in Ihrem onBlurHandler wie folgt verwenden:

<input id="input" onblur="removeMenu()" ... />

function removeMenu() {
    setInterval(function(){
        if (!mouseflag) {
            document.getElementById('menu').innerHTML = '';
        }
    }, 0);
}

Die setIntervalFunktion entfernt Ihre onBlur Funktion aus dem Aufrufstapel. Fügen Sie hinzu, da Sie die Zeit auf 0 gesetzt haben. Diese Funktion wird sofort aufgerufen, nachdem ein anderer Ereignishandler beendet wurde

user5271069
quelle
5
setIntervalist eine schlechte Idee, Sie brechen es nie ab, so dass es Ihre Funktion kontinuierlich ausführt und höchstwahrscheinlich dazu führt, dass Ihre Site die maximale CPU verwendet. Verwenden Sie setTimeoutstattdessen, wenn Sie diese Technik verwenden möchten (was ich nicht empfehle). Es ist ein riskantes Geschäft, Ihre App vom Zeitpunkt bestimmter Ereignisse abhängig zu machen.
Casey
6
GEFAHR WIRD ROBINSON! ACHTUNG! Rennbedingungen voraus!
GoreDefex
Ich habe diese Lösung ursprünglich entwickelt (naja, setTimeoutnicht setInterval), aber ich musste sie auf 250ms erhöhen, bevor das Timing in der richtigen Reihenfolge erfolgt. Dieser Fehler wird wahrscheinlich auch auf langsameren Geräten bestehen bleiben und macht die Verzögerung spürbar. Ich habe es versucht onMouseDownund es funktioniert gut. Ich frage mich, ob es auch die Touch-Events braucht.
Brennan Cheung
So etwas wie ein Intervall ist nicht schlecht, wenn Sie onClick wirklich verwenden möchten. Sie möchten jedoch ein rekursives Zeitlimit mit einer Bedingung anstelle eines Intervalls, damit es nicht für immer ausgeführt wird. Dies ist das gleiche Muster, das bei bedingten Selen-Wartezeiten verwendet wird.
Will Cain