The following is a guest post by Alessandro Vendruscolo. Media queries are relevant to both CSS and JS. The need and desire to manage those in one place is real. There have been some clever ways to do this, like Jeremy Keith’s Conditional CSS. But in that case, the onus is on you to test after window state changes. You can get a true listener with MediaQueryList, but then you’re maintaining the media queries in both places again. Ah well, I’ll let Alessandro explain.
Media queries were first introduced more than twelve years ago (yes, the first draft dates back to 4 April 2001!) and were introduced to limit the scope of a style sheet
A media query consists of a media type and one or more expressions to limit the scope of a certain style sheet. […] By using media queries, content presentations can be tailored to a range of devices without changing the content itself.
With media queries in CSS we can selectively use styles depending on, for example, the width of browser screen
@media screen and (min-width: 960px) {
body {
padding: 50px;
}
}
In the example above, if your browser screen is wider than or equal to 960px, the body will have 50px of padding.
DOM-Ereignisse ausgelöst durch CSS-Animationen
Ein einfacher Klick-Event
document.querySelector('a.button').addEventListener('click', function(event) {
// do something
});
Nichts Besonderes hier, nur Standard-DOM-Programmierung. Das click-Ereignis, zusammen mit vielen anderen Ereignissen, wird ausgelöst, wenn der Benutzer mit der Seite interagiert: klickt, bewegt den Mauszeiger, scrollt die Seite usw.
Alle diese Ereignisse sind mit dem DOM verbunden und entstehen als Folge von Benutzerinteraktionen. Es gibt auch einige Ereignisse, die speziell mit CSS Animationen zusammenhängen: animationStart, animationEnd (und ihre Transition-Verwandten, transitionStart, transitionEnd).
Wenn wir ein neues Element auf die Seite einfügen und es eine Animation hat, wissen wir, dass die CSS-Animation sofort nach dem Einfügen beginnt. Wenn wir also auf das `animationstart`-Ereignis dieser Animation warten, können wir wissen, wann sie eingefügt wurde.
@keyframes nodeInserted {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
.an-element {
animation-duration: 0.001s;
animation-name: nodeInserted;
}
document.addEventListener("animationstart", function (event) {
if (event.animationName == "nodeInserted") {
// an element with class an-element has been inserted in the DOM
}
}, false);
Danke an David Walsh, Daniel Buchner und Omar Ismail für das Bloggen darüber vor etwa einem Jahr.
Medienabfragen und Animationen kombinieren
Vielleicht können Sie erahnen, wohin das führt. Wenn wir die Animation auslösen, wenn sich eine Medienabfrage ändert (anstatt beim Einfügen von Knoten), können wir auch in unserem JavaScript erkennen, wann diese Änderung der Medienabfrage stattgefunden hat.
body {
animation-duration: 0.001s;
}
@media screen and (min-width: 1000px) {
body {
animation-name: min-width-1000px;
}
}
@media screen and (min-width: 700px) {
body {
animation-name: min-width-700px;
}
}
@keyframes min-width-700px {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
@keyframes min-width-1000px {
from { clip: rect(1px, auto, auto, auto); }
to { clip: rect(0px, auto, auto, auto); }
}
document.addEventListener(animationEnd, dispatchEvent, false);
// check the animation name and operate accordingly
function dispatchEvent(event) {
if (event.animationName === 'min-width-700px') {
document.body.innerHTML = 'Min width is 700px';
} else if (event.animationName === 'min-width-1000px') {
document.body.innerHTML = 'Min width is 1000px';
}
}
Hinweis des Herausgebers: Wählen Sie Ihre Namen weise.
Wir haben die gleiche einfache Animation, die eigentlich nichts tut, aber dieses Mal ist die Animation innerhalb eines @media-Blocks.
Wenn das Fenster breiter als 700px wird, wird die Animation min-width-700px ausgelöst und unser dispatchEvent erhält diese Information. Stellen Sie sich das wie einen Callback vor. Wir können dann unsere Seite anpassen, indem wir neue Widgets anzeigen, andere zerstören oder was auch immer Sie tun müssen, um uns an die neue Bildschirmgröße anzupassen.
Das Coole daran ist, dass wir das `window.resize`-Ereignis nicht verwenden müssen, das langsam sein kann und zu oft ausgelöst wird (Throttling ist normalerweise erforderlich). Das animationstart-Ereignis wird ausgelöst, sobald die Medienabfrage übereinstimmt, auch wenn wir das Fenster nicht vergrößert haben. Wenn wir dann das Fenster vergrößern, wird das Ereignis genau dann ausgelöst, wenn die Medienabfrage übereinstimmt.
Der richtige Weg, diese Dinge zu tun
Jetzt haben wir darüber gesprochen, wie man JavaScript-Callbacks für Änderungen der Fensterbreite erhält, ohne sich auf das resize-Ereignis zu verlassen.
Ich würde Ihnen jedoch nicht empfehlen, dies in Produktionscode zu verwenden. Es funktioniert, aber der richtige Weg, dies zu tun, wäre die Verwendung der matchMedia-Methode, die eine MediaQueryList zurückgibt. MediaQueryList-Objekte können Listener haben, und somit können wir die addListener-Methode verwenden, um benachrichtigt zu werden, wenn eine Medienabfrage übereinstimmt oder nicht.
Enquire.js hat kürzlich Version 2 erreicht und wickelt die matchMedia-Methode ein, was uns einen sauberen und robusten Wrapper bietet.
Darf ich fragen, warum Sie nicht empfehlen, dies im Produktionscode zu verwenden… Ich mag Enquire.js, aber seine Abhängigkeit von MatchMedia bedeutet meiner Meinung nach, dass es in IE9 möglicherweise nicht funktioniert…
IE 10 unterstützt matchMedia, aber dieser Browser ist in Bezug auf IE recht neu und es könnte eine Weile dauern, bis viele Windows-Benutzer auf IE 10 aktualisieren…
Ihre Lösung würde meiner Meinung nach bei den meisten modernen Browsern funktionieren… Warum also nicht im Produktionscode verwenden?
Dies beruht auf CSS-Animationen, die in IE9 nicht unterstützt werden. Außerdem gibt es ein Polyfill für MatchMedia, sodass Sie enquire.js trotzdem in älteren Browsern verwenden können.
Ich würde es nicht in der Produktion verwenden, nur um das richtige Werkzeug für den richtigen Job zu verwenden. Dieser Hack funktioniert, aber es ist immer noch ein Hack!
Es erfordert, dass Sie eine Animation erstellen, nur um eine Medienabfrageübereinstimmung zu erkennen. MatchMedia ist das Richtige für diese Art von Aufgabe.
Enquire-Autor hier :)
Alessandro hat Recht, es ist besser,
matchMediazu verwenden, da es genau für diesen Zweck entwickelt wurde. Wenn Sie ein Polyfill für weniger fähige Browser bereitstellen, haben Sie keine Probleme.Das traditionelle matchMedia polyfill von Scott Jehl & Paul Irish ermöglicht Browsern, die CSS3-Medienabfragen unterstützen, aber nicht über die JS
matchMedia-API verfügen, dass sie einwandfrei mitmatchMediafunktionieren. Dies bedeutet jedoch, dass es nur für Browser mit CSS3-Medienabfrageunterstützung funktioniert – IE9 und Opera 12.0 fallen in diese Kategorie.Wenn Sie mehr Browserunterstützung benötigen, sollten Sie ein Polyfill mit tieferer Unterstützung in Betracht ziehen, das im Grunde CSS3-Medienabfragen vollständig emuliert. Ein großartiges Beispiel hierfür ist David Knights media-match. Es funktioniert bis zurück zu IE6! Dies hat natürlich seinen Preis in Bezug auf die Dateigröße, da dieses Polyfill viel mehr tut als das vorherige Polyfill.
Also, entscheiden Sie über die Browserunterstützung, wählen Sie ein geeignetes Polyfill, um dies zu erreichen, und los geht's!
Das ist ziemlich großartig und alles, aber kann mir jemand eine reale Instanz anbieten, für die dies praktisch wäre, damit ich es mir ein paar Stunden lang erklären kann, um es wirklich zu verstehen? Danke!
Nette Tricks. Ich habe vor kurzem mqa.js (media query aliases) für diesen Zweck veröffentlicht. Es funktioniert gut mit Polyfills, sodass IE9 mit Polyfill hervorragend funktioniert. Der Grund für das Projekt ist eher wartungsorientiert. Sie möchten nicht dieselbe Medienabfrage über Ihre CSS- und JS-Dateien verteilen, mqa.js mildert dies, indem es einen benutzerdefinierten ID-Selektor in der Medienabfrage verwendet, diesen parst und Ereignisse mit dem Alias in JavaScript auslöst. Weitere Informationen finden Sie auf meiner GitHub-Seite und in einem Blogbeitrag, der es etwas genauer erklärt.
Danke!
Interessante Einsicht und gute Idee.
Nachdem der Trick mit der Node-DOM-Einfügung herauskam, dachte ich darüber nach, wie er verwendet werden kann – Klassenänderungen sind das Erste, was mir in den Sinn kam.
Was mich davon abhielt, ihn zu nutzen, war, dass er von IE9 nicht unterstützt wird.
Ich hatte Jeremy Keiths Conditional CSS-Artikel nie gesehen, aber die Lösung, die ich gefunden habe und über die ich letzte Woche gebloggt habe, ist im Grunde dieselbe. In meinem Fall wende ich einfach eine Farbe auf ein Element an, das versteckt ist, und bestimme den „Breakpoint-Level“ einfach. Durch die Verwendung von Hex-Farben wie #000000, #010101, #020202 usw. sind meine Breakpoints 0, 1, 2 usw.
Ich habe mich vor kurzem gefragt, wie das funktioniert… danke für die Aufschlüsselung!
Ja… ich habe einige davon auf meiner Website http://pixelgenio.com verwendet. Ich erstelle animierte Videos, aber ich interessiere mich auch sehr für CSS.
Es gibt etwas, das ich nicht verstehe. Wenn es kein Mittel ist, das seinen Platz im Produktionscode hat, warum wird es dann überhaupt angepriesen?
Tolle Idee!
Nun, Animationsevents werden nicht gut unterstützt. Ich schlage vor, Sie verwenden Transitionsevents, die eine viel bessere Unterstützung haben. Es ist auch besser, Transitionen zu verwenden, da Sie keine gefälschte Animation erstellen müssen, Sie können die Eigenschaft wiederverwenden, die Sie in Ihrer Medienabfrage für die Transition verwenden.