Media Query Change Detection in JavaScript Through CSS Animations

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

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.

Demo auf CodePen


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.