Wie simuliere ich ein Mouseover in reinem JavaScript, das das CSS ": hover" aktiviert?

82

Ich habe versucht, Code zu finden, der mouseoverin Chrome simuliert werden kann, aber obwohl der Listener "mouseover" ausgelöst wird, wird die CSS-Deklaration "hover" nie festgelegt!

Ich habe auch versucht:

//Called within mouseover listener
theElement.classList.add("hover");

Aber nichts scheint das Element an dem zu ändern, was in seiner hoverDeklaration deklariert ist .

Ist das möglich?

Don Rhummy
quelle
6
@PSL Ich denke, er möchte :hovereinem Element den Status aufzwingen .
Benjamin Gruenbaum
1
@BenjaminGruenbaum Ja, du hast recht. Ich habe es falsch verstanden.
PSL
1
Dies ist kein Duplikat. Die andere Frage betrifft jQuery. Diese Frage handelt von reinem JS.
Chuck Le Butt
1
@Monk Der OQ enthielt das jQuery-Tag. Dies ist ein Duplikat.
JasonMArcher
1
@JasonMArcher Die Frage (und ihre Antwort) handelt von reinem JS. Glaubst du nicht, dass es wahrscheinlicher ist, dass sie es falsch verstanden haben?
Chuck Le Butt

Antworten:

106

Das kannst du nicht. Es ist kein vertrauenswürdiges Ereignis .

Ereignisse, die vom Benutzeragenten entweder als Ergebnis einer Benutzerinteraktion oder als direktes Ergebnis von Änderungen am DOM generiert werden, werden vom Benutzeragenten mit Berechtigungen vertraut, die für Ereignisse, die vom Skript über DocumentEvent.createEvent generiert wurden, nicht gewährt werden ("Event") -Methode, die mit der Event.initEvent () -Methode geändert oder über die EventTarget.dispatchEvent () -Methode ausgelöst wurde. Das isTrusted-Attribut von vertrauenswürdigen Ereignissen hat den Wert true, während nicht vertrauenswürdige Ereignisse den isTrusted-Attributwert false haben.

Die meisten nicht vertrauenswürdigen Ereignisse sollten keine Standardaktionen auslösen , mit Ausnahme von Klick- oder DOMActivate-Ereignissen.

Sie müssen eine Klasse hinzufügen und diese manuell zu den Mouseover- / Mouseout-Ereignissen hinzufügen / entfernen.

Benjamin Gruenbaum
quelle
4
@ Tim, es antwortet eigentlich nicht warum . Es ändert nur die Frage .
Pacerier
7
@ Pacerier ist kein vertrauenswürdiges Ereignis, da es nicht vom Benutzer initiiert wurde. Das steht genau dort im obigen Zitat in meiner Antwort. Es ist buchstäblich, womit die Antwort beginnt.
Benjamin Gruenbaum
1
@BenjaminGruenbaum, ich zitiere "mit Ausnahme von Klick- oder DOMActivate-Ereignissen" . Was ist das Besondere an "Hover", dass es nicht in dieser Ausnahmeliste enthalten ist? Warum dürfen wir "Fokus" nennen, aber nicht "Schweben"? Dies sind die Fragen, die wir beantworten müssen. Wenn wir etwas anderes beantworten, ändert sich nur die Frage .
Pacerier
1
Ich würde davon ausgehen, dass Hover (und in Kombination mit Klicken oder Maus auf und ab für Drag & Drop) sich wie das Entführen eines Benutzersystems verhalten oder aussehen könnte (wenn Sie Automatisierungsfälle ignorieren), daher ist es nicht ideal für die Unterstützung. Klicken Sie auf Ich kann nicht sagen, aber der Fokus würde den Autofokus auf ein Zielformularfeld oder -element ermöglichen, auf das sich der Site-Designer beispielsweise auf ein Textfeld mit fehlenden oder falschen Daten konzentrieren soll (z. B. Überprüfungen der Formularfeldvalidierung). Aber das sind nur meine Annahmen.
David
2
Ja, so absurd es auch klingen mag, Sie können den Eindruck erwecken, dass der Fokus durch eine Benutzerinteraktion verursacht wurde, bei der dies nicht der Fall war. In der Praxis missachten Browser-APIs gerne die Spezifikation und ignorieren den Fokus und klicken als nicht vertrauenswürdige Ereignisse (zumindest Chrome). Versuchen Sie zum Beispiel, einen Klick auf eine Schaltfläche zum Installieren einer Erweiterung für eine Chrome-Erweiterung zu simulieren, und sehen Sie, was passiert :)
Benjamin Gruenbaum
37

Sie können das Mouseover-Ereignis folgendermaßen simulieren:

HTML

<div id="name">My Name</div>

JavaScript

var element = document.getElementById('name');
element.addEventListener('mouseover', function() {
  console.log('Event triggered');
});

var event = new MouseEvent('mouseover', {
  'view': window,
  'bubbles': true,
  'cancelable': true
});

element.dispatchEvent(event);
lbpeppers
quelle
Danke, alles was ich brauchte waren Blasen! Klicken Sie im Inspektor der Entwicklerkonsole mit der rechten Maustaste auf ein Element und führen Sie "In Konsole verwenden" (Firefox) oder "Als globale Variable speichern" (Chrome) aus. Dann können Sie zB temp0.dispatchEvent(new MouseEvent('mouseover', {bubbles: true})) sehr nützlich sein, um Tooltips oder andere Inhalte zu gestalten, die nur mit der Maus angezeigt werden.
Denis Howe
Das ist fantastisch.
Rebelli
1
@DenisHowe Für diesen Anwendungsfall sollten Sie die :hovSchaltfläche oben im Stilinspektor in devtools überprüfen. Hiermit können Sie eine beliebige Pseudoklasse für das zu untersuchende Element aktivieren.
Hallo
17

Hintergrund

Ich bin auf diese Frage gestoßen, als ich versucht habe, automatisierte Tests zu schreiben, um zu überprüfen, ob ein bestimmter Satz von Elementen auf einer bestimmten Seite, die alle empfangen, die vom CSS für On-Hover-Ereignisse festgelegten CSS-Eigenschaften aufweist.

Während die obige Antwort perfekt erklärt, warum es nicht möglich ist, das Hover-Ereignis einfach durch JS auszulösen und dann einen CSS-Wert von Interesse zu prüfen, beantwortet sie die anfängliche Frage "Wie simuliere ich ein Mouseover in reinem JavaScript, das das CSS aktiviert?" :schweben"?" nur teilweise.

Haftungsausschluss

Dies ist keine performante Lösung. Wir verwenden es nur für automatisierte Tests, bei denen die Leistung keine Rolle spielt.

Lösung

simulateCssEvent = function(type){
    var id = 'simulatedStyle';

    var generateEvent = function(selector){
        var style = "";
        for (var i in document.styleSheets) {
            var rules = document.styleSheets[i].cssRules;
            for (var r in rules) {
                if(rules[r].cssText && rules[r].selectorText){
                    if(rules[r].selectorText.indexOf(selector) > -1){
                        var regex = new RegExp(selector,"g")
                        var text = rules[r].cssText.replace(regex,"");
                        style += text+"\n";
                    }
                }
            }
        }
        $("head").append("<style id="+id+">"+style+"</style>");
    };

    var stopEvent = function(){
        $("#"+id).remove();
    };

    switch(type) {
        case "hover":
            return generateEvent(":hover");
        case "stop":
            return stopEvent();
    }
}

Erläuterung

generateEvent liest alle CSS-Dateien, ersetzt: Hover durch eine leere Zeichenfolge und wendet sie an. Dies hat zur Folge, dass alle: Hover-Stile angewendet werden. Jetzt kann man nach einem verwöhnten Stil suchen und durch Stoppen der Simulation in den Ausgangszustand zurückkehren.

Warum wenden wir den Hover-Effekt für das gesamte Dokument und nicht nur für das interessierende Element an, indem wir den aus den Blättern abrufen und dann eine element.css (...) ausführen?

Wenn Sie dies tun, wird der Stil inline angewendet. Dies würde andere Stile überschreiben, die möglicherweise nicht vom ursprünglichen CSS-Hover-Stil überschrieben werden.

Wie würde ich jetzt den Schwebeflug für ein einzelnes Element simulieren?

Dies ist nicht performant, also besser nicht. Wenn Sie müssen, können Sie mit element.is (selectorOfInterest) prüfen, ob der Stil für Ihr Element gilt, und nur diese Stile verwenden.

Beispiel

In Jasmine können Sie zB jetzt Folgendes ausführen:

describe("Simulate CSS Event", function() {
    it("Simulate Link Hover", function () {
      expect($("a").css("text-decoration")).toBe("none");
      simulateCssEvent('hover');
      expect($("a").css("text-decoration")).toBe("underline");
      simulateCssEvent('stop');
      expect($("a").css("text-decoration")).toBe("none");
    });
});
Amstutz
quelle
1
Diese Antwort ist großartig! Leider schlägt dies in neueren Versionen von Chrome auf Seiten mit Stylesheets fehl, die von anderen Hosts bereitgestellt werden, da Chrome den Zugriff auf originensübergreifende Anforderungen einschränkt. Siehe meine Antwort für eine Lösung, die dies unterstützt :)
FThompson
6

Was ich in der Regel in diesem Fall zu tun ist das Hinzufügen einer Klasse mit Hilfe von Javascript .. und Anbringen der gleichen CSSwie die :hoverfür diese Klasse

Versuchen Sie es mit

theElement.addEventListener('onmouseover', 
    function(){ theElement.className += ' hovered' });

Oder für ältere Browser:

theElement.onmouseover = function(){theElement.className += ' hovered'};

Sie müssen natürlich verwenden onmouseout, um die "schwebende" Klasse zu entfernen, wenn Sie das Element verlassen ...

Yotam Omer
quelle
Dies wird nicht das tun, was das OP verlangt, obwohl es wahrscheinlich mehr oder weniger in die richtige Richtung geht. Es wäre besser, moderne Anbringungstechniken für Event-Handler zu verwenden.
Pointy
Entschuldigung, missverstandene Frage
Yotam Omer
@Pointy die Frage fragt nach reinem Javascript .. wie können die Ereignisse sonst angehängt werden?
Yotam Omer
2
Nicht gut genug? Es ist "reines" JavaScript und fügt tatsächlich eine Funktion als Handler für ein Ereignis hinzu. In Internet Explorer würde das (fast) Äquivalent attachEvent()verwendet.
Pointy
Erwähnenswert. IE9 und 10 unterstützen addEventListenerund es ist der beste Weg, Ereignisse anzuhängen. Shimming attachEventist nur in IE8 (und darunter) erforderlich. Yotam, wenn er einen weiteren Handler hinzufügt, onmouseoverindem er das Attribut direkt setzt (anstatt den Ereignis-Listener hinzuzufügen), wird das aktuell eingestellte Ereignis überschrieben. Siehe diese Frage zum Unterschied.
Benjamin Gruenbaum
4

Sie können pseudo: styler verwenden , eine Bibliothek, die CSS-Pseudoklassen auf Elemente anwenden kann.

(async () => {
  let styler = new PseudoStyler();
  await styler.loadDocumentStyles();
  document.getElementById('button').addEventListener('click', () => {
    const element = document.getElementById('test')
    styler.toggleStyle(element, ':hover');
  })
})();

Haftungsausschluss: Ich bin Mitautor dieser Bibliothek. Wir haben es so konzipiert, dass es zusätzlich originalübergreifende Stylesheets unterstützt, insbesondere für die Verwendung in Chrome-Erweiterungen, bei denen Sie wahrscheinlich keine Kontrolle über die CSS-Regeln der Seite haben.

FThompson
quelle
1
Dies scheint tatsächlich ziemlich gut zu funktionieren, selbst wenn es mit window.getComputedStyle(<youElement>)dem Pseudo-Stil kombiniert wird . Vielen Dank!
Daniel Veihelmann
2

Ich gehe davon aus, dass Sie das CSS nach der Dom-Manipulation überprüfen möchten, aber sobald Sie Ihre Maus zurück zu den devtools bewegen, ist das Ereignis für dieses HTML-Element nicht mehr aktiv. Sie möchten wahrscheinlich so etwas wie die Option: hover in devtools für Ihre Javascript-Ereignisse haben. Das gibt es nicht, aber Sie können es simulieren.

  1. Öffnen Sie Ihre Devtools und klicken Sie darauf, um sie zu aktivieren.
  2. Lösen Sie das Ereignis für das Element aus, an dem Sie interessiert sind.
  3. Öffnen Sie ohne die Maus zu bewegen das devtools-Befehlsfeld mit Strg + Umschalt + p und wählen Sie mit Ihrer Tastatur die Option "Javascript deaktivieren".

Da Javascript deaktiviert ist, besteht keine Möglichkeit, die Elemente wieder zu ändern. Sie können zu den Devtools gehen und CSS und HTML überprüfen, als ob Sie schweben, klicken oder etwas anderes damit machen würden. Wenn Sie fertig sind, rufen Sie erneut das Befehlsfeld auf und wählen Sie "Javascript aktivieren".

Piepongwong
quelle
Ordentlich Technik, aber wenn ich nicht Ihre Absicht falsch verstanden, das nicht existiert bereits in devtools! In den Devtools von Chrome / Chromium und Firefox befindet sich oben im Stilinspektor eine :hovSchaltfläche (neben dem Eingabefeld "Filter"). Durch Klicken :hoverweitert eine Reihe von Kontrollkästchen, die jeweils mit einer Pseudo gekennzeichnet: :active, :focus, :focus-within, :hover, und :visited. Wenn Sie eines dieser Kontrollkästchen aktivieren, wird die entsprechende Pseudoklasse für das zu untersuchende Element aktiviert.
Hallo