Activer vos effets JavaScript en fonction de vos Media Queries

Je viens de voir un code allembiqué permettant de maintenir la hauteur de deux <div> côte à côte. Problème ? Ces éléments ne sont pas côte à côte en version mobile mais l'un sous l'autre : et maintenir la hauteur dans ce cas là ne sert à rien. La semaine d'avant, j'ai vu un code avec une cascade de if permettant d'ouvrir une vidéo dans une popup sur PC et d'ouvrir un lien Youtube sur mobile. Bien évidemment, les petits écrans PC ouvraient une popup alors qu'il aurait été intéressant qu'ils ouvrent aussi un lien. Je vous fais l'impasse sur les comportements au redimensionnement de la fenêtre.

Bref, lançons-nous dans un petit exercice pour permettre un code JavaScript Responsive Web Design, sans se soucier de l'appareil qui l’exécute.

La fonction JavaScript window.matchMedia

De la même manière que dans une feuille CSS nous allons nous servir de @media, en JavaScript, nous allons nous servir de window.matchMedia pour adresser différent comportement en fonction de la taille d'affichage de l'écran. La syntaxe dans les deux cas est identique et une simple ligne gère la condition total d'affichage. Enfin, si votre navigateur ne gère pas window.matchMedia, il ne gère pas non plus @media ce qui va nous arranger pour le support.

Notre problématique

Je souhaite ouvrir la vidéo d'une chaine Youtube dans une popup. Si l'écran est trop petit, je préfère allez sur la page Internet de la page. Cela permet aux smartphones de proposer d'ouvrir le lien Internet dans une application tierce dédiée (entre autre) et aux petits écrans de ne pas se manger de popup.

HTML

Tout d'abord, préparons le terrain HTML. Nous avons besoin de deux box côte à côte, la première présentera la chaine Youtube, la seconde permettra de lancer la vidéo dans une popup. Si l'écran est trop petit, les box seront l'une sous l'autre.

<section class="youtube-presentation">
  <div class="presentation">
    <h1>Youtube for Playstation</h1>
    <h2>Description</h2>
    <p>Welcome to the official home of PlayStation on YouTube.</p>
    <p>Here you'll find the latest videos about your favorite
    PlayStation products direct from Sony Computer Entertainment,
    as well as consumer generated clips from other PlayStation
    fans. To stay up to date, be sure to subscribe above and
    check back often for the new game trailers,
    behind-the-scenes footage, in-game moments,
    tips and tricks and exclusive sneak peeks.</p>
  </div>
  <div class="video">
    <h2>Final Fantasy VII Remake E3 Trailer | PS4</h2>
    <div class="link">
      <a 
        href="https://www.youtube.com/watch?v=Kznek1uNVsg"><img 
        src="https://lh3.googleusercontent.com/[...]" 
        alt="Final Fantasy VII Remake E3 Trailer | PS4"></a>
    </div>
    <div class="popup">
      <div class="embeded">
        <div class="close"></div>
        <iframe 
          width="560" 
          height="315" 
          src="https://www.youtube.com/embed/Kznek1uNVsg" 
          frameborder="0" 
          allowfullscreen></iframe>
      </div>
    </div>
  </div>
</section>

CSS

Habillons tout ça :

/* On créer un container responsive */
* {
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}
.youtube-presentation {
  margin-left: -1rem;
  margin-right: -1rem;
  margin-top: 1rem;
  margin-bottom: 1rem;
}
.youtube-presentation:after {
  content: '';
  clear: left;
  display: block;
}

/* Et deux colonnes Responsive */
.youtube-presentation .presentation,
.youtube-presentation .video {
   padding: 1rem;
   float: left;
   width: calc(100% - 2rem);
   background-color: #e0e0e0;
   margin: 1rem;
   margin: 1rem;
}
@media (min-width: 768px) {
  .youtube-presentation .presentation,
  .youtube-presentation .video {
     width: calc(50% - 2rem);
  }
}

/* Mise en forme de la présentation */
.youtube-presentation .presentation *:first-child {
  margin-top: 0;
}
.youtube-presentation .presentation *:last-child {
  margin-bottom: 0;
}

/* Mise en forme de l'image vidéo */
.youtube-presentation .video *:first-child {
  margin-top: 0;
}
.youtube-presentation .video .link {
  max-width: 100%;  
}
.youtube-presentation .video .link img {
  max-width: 100%;
  background-color: #000;
  padding-top: 1rem;
  padding-bottom: 1rem;
  margin: 0;
  margin-top: 1rem;
}

/* Mise en forme de la popup pour la vidéo */
.youtube-presentation .video .popup {
  background-color: rgba(0, 0, 0, 0.8);
}
.youtube-presentation .video iframe {
  position: absolute;
  width: 100%;
  height: 100%;
}

/* Affichage de la popup */
.youtube-presentation .popup {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
@media (min-width: 768px) {
  .youtube-presentation .popup.opened {
    display: block;
  }
}
.youtube-presentation .popup .embeded {
  position: absolute;
  width: 50%;
  height: 50%;
  top: 50%;
  left: 50%;
  -webkit-transform: translateX(-50%) translateY(-50%);
     -moz-transform: translateX(-50%) translateY(-50%);
      -ms-transform: translateX(-50%) translateY(-50%);
       -o-transform: translateX(-50%) translateY(-50%);
          transform: translateX(-50%) translateY(-50%);
}
.youtube-presentation .popup .close {
  position:absolute;
  right: 0;
  bottom: 100%;
  cursor: pointer;
}
.youtube-presentation .popup .close:before {
  content: 'X';
  display: block;
  color: #000;
  -webkit-border-radius: 50%;
          border-radius: 50%;
  cursor: pointer;
  padding: .2rem;
  font-size: 1rem;
  width: 2rem;
  text-align: center;
  background-color: #fff;
  margin-bottom: .2rem;
}

Et voyons le résultat !

Welcome to the official home of PlayStation on YouTube.

Here you'll find the latest videos about your favorite PlayStation products direct from Sony Computer Entertainment, as well as consumer generated clips from other PlayStation fans. To stay up to date, be sure to subscribe above and check back often for the new game trailers, behind-the-scenes footage, in-game moments, tips and tricks and exclusive sneak peeks.

Conclusion : Sans JavaScript nous avons été capable d'habiller la totalité des éléments et de créer l'affichage de la popup (que l'on ne voit pas sans la classe opened). Nous allons maintenant avoir du JavaScript pour

  • maintenir la hauteur des box à la même taille, uniquement quand ils sont côte à côte,
  • ouvrir la popup en grand format ou laisser le lien amener à une page Youtube.

Notre solution : window.matchMedia

Commençons dans un premier temps, à l'aide de jQuery, par appliquer le script qui va maintenir la hauteur, et ouvrir la popup.

/* Variable globale si pas encore défini */
var website = website || {},
    $window = $window || $(window);

/* 
 * Scope pour nos deux nouvelles fonctions, 
 * aucune variable ici ne polluera le reste
 * des scripts.
 */
(function (publics) {

  /* 
   * Création de la fonctionnalité pour maintenir 
   * les hauteurs.
   */
  publics.sameHeight = function ($items) {

    function sameHeight() {
      var maxHeight = 0;

      /* Parmi les éléments recherchés... */
      $items.height("").each(function () {
        var $current = $(this);

        /* ...on trouve le plus haut... */
        if ($current.height() > maxHeight) {
          maxHeight = $current.height();
        }

      /* Et on applique cette hauteur à tous les autres. */
      }).height(maxHeight);
    }

    /* 
     * Application de la fonction à la 
     * lecture de celle-ci...
     */
    sameHeight();

    /* 
     * ...puis à chaque redimensionnement de 
     * page.
     */
    $window.resize(function () {
      sameHeight(); 
    });
  };

  /* 
   * Création de la fonctionnalité pour ouvrir/fermer 
   * les popups.
   */
  publics.popupVideo = function ($senders, $targets) {

    /* Sur une popup ouverte... */
    function closePopup($popup) {
      /* ...permettre au clique sur le background qu'elle se ferme... */
      $popup.click(function (e) {
        e.preventDefault();
        $popup.removeClass("opened")

      /* ...mais pas que le contenu utile la ferme... */
      }).find(".content").click(function (e) {
        e.stopPropagation();

      /* ...mais que le bouton close la ferme. */
      }).find(".close").click(function (e) {
        e.preventDefault();
        $popup.removeClass("opened")
      });
    }

    /* Chaque élément d'ouverture $sender... */
    function popupVideo() {
      $senders.each(function (i) {
        var $sender = $(this),
            $target = $targets.eq(i);

        /* ...cible sa popup associée $target */
        $sender.click(function (e) {
          /* ...l'ouvre au clique... */
          e.preventDefault();
          $target.addClass("opened");
        });

        /* ...et met en place sa fermeture. */
        closePopup($target)
      });
    }

    /* 
     * Application de la fonction à la 
     * lecture de celle-ci...
     */
    popupVideo();

    /* 
     * ...puis à chaque redimensionnement de 
     * page.
     */
    $window.resize(function () {
      popupVideo(); 
    });
  };

}(website));

/* 
 * Application de nos deux fonctions sur le 
 * HTML préparé en amont.
 */
website.sameHeight(
  $(".youtube-presentation .presentation, .youtube-presentation .video")
);
website.popupVideo(
  $(".youtube-presentation .video a"),
  $(".youtube-presentation .popup")
);

Ce qui nous donne au final... ceci !

Welcome to the official home of PlayStation on YouTube.

Here you'll find the latest videos about your favorite PlayStation products direct from Sony Computer Entertainment, as well as consumer generated clips from other PlayStation fans. To stay up to date, be sure to subscribe above and check back often for the new game trailers, behind-the-scenes footage, in-game moments, tips and tricks and exclusive sneak peeks.

Les mécanismes sont en place et fonctionnels, cependant, au vu du design de ma page dans ses divers formats Responsive, nous allons :

  • Pouvoir changer les règles pour les Media Queries @media de ma feuille CSS.
  • Les implémenter également dans le JavaScript avec window.matchMedia.

Le code JavaScript final

Au regard de l'affichage de ma zone de contenu, il y a assez de place pour afficher les box côte à côte pour une largeur de fenêtre de 992px à l'infini et entre 480px et 780px. En ce qui concerne l'affichage de la popup, en dessous de 480px de large, je préférerais afficher la vidéo sur une page dédiée Youtube ou dans une application tierce. Je veux également le même comportement si l'affichage est inférieur à 320px de hauteur.

Les deux Media Queries seront donc :

  • Pour les box : (min-width: 480px) AND (max-width: 779px), (min-width: 992px)
  • Pour la popup : (min-width: 480px) AND (min-height: 320px)

Note : Les valeurs données étant incluses, je mets donc 779px et non 780px pour qu'à 780px les box soient de nouveau en ligne.

Cela nous donne les modifications suivantes pour la CSS :

CSS

/* ... */

/* Et deux colonnes Responsive */
.youtube-presentation .presentation,
.youtube-presentation .video {
   padding: 1rem;
   float: left;
   width: calc(100% - 2rem);
   background-color: #e0e0e0;
   margin: 1rem;
   margin: 1rem;
}
@media (min-width: 480px) AND (max-width: 779px), (min-width: 992px) { /* Media Queries des box. */
  .youtube-presentation .presentation,
  .youtube-presentation .video {
     width: calc(50% - 2rem);
  }
}

/* ... */

/* Affichage de la popup */
.youtube-presentation .popup {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
@media (min-width:480px) AND (min-height: 320px) { /* Media Queries de la popup. */
  .youtube-presentation .popup.opened {
    display: block;
  }
}

/* ... */

et les modifications suivantes pour le JavaScript :

/* ... */

(function (publics) {
  var privates = {};

  /*
   * Nous créons une petite fonctionnalité
   * utile uniquement dans notre scope pour
   * autoriser, en fonction de la Media Queries
   * le JavaScript à faire effet.
   */
  privates.allowMechanism = function (mediaQueries) {
    var rtrn = false;

    /*
     * Si le navigateur ne gère pas les Media Queries,
     * la fonctionnalité ne fait rien de notable.
     */
    if (typeof mediaQueries !== 'undefined') {

      /*
       * Si il les gère, appliquer l'effet, uniquement
       * sur ce qui a été demandé dans la Media Queries.
       */
      if (typeof window.matchMedia !== 'undefined') {
       rtrn = window.matchMedia(mediaQueries).matches;

      /*
       * Si il les gère, mais qu'aucune Media Queries n'a été
       * définies, rendre l'effet pour touts les
       * cas de figure.
       */
      } else {
        rtrn = true;
      }
    }

    return rtrn;
  }

  publics.sameHeight = function ($items, mediaQueries) {

    function sameHeight() {
      var maxHeight = 0;
      $items.height("");

      /*
       * On ne manage la hauteur des box que
       * si les Media Queries sont validées.
       */
      if (privates.allowMechanism(mediaQueries)) {
        $items.each(function () {
          var $current = $(this);

          if ($current.height() > maxHeight) {
            maxHeight = $current.height();
          }
        }).height(maxHeight);
      }
    }

    /* ... */
  };

  publics.popupVideo = function ($senders, $targets, mediaQueries) {

    /* ... */

    function popupVideo() {
        /* ... */

        $sender.click(function (e) {

          /*
           * On autorise l'ouverture de la Popup uniquement
           * si les Media Queries sont validées.
           */
          if (privates.allowMechanism(mediaQueries)) {
            e.preventDefault();
            $target.addClass("opened");
          }
        });

        /* ... */
      });
    }

    /* ... */
  };

}(website));

/* 
 * Application de nos deux fonctions sur le 
 * HTML préparé en amont.
 */
website.sameHeight(
  $(".youtube-presentation .presentation, .youtube-presentation .video"),
  "(min-width: 480px) AND (max-width: 779px), (min-width: 992px)"
);
website.popupVideo(
  $(".youtube-presentation .video a"),
  $(".youtube-presentation .popup"),
  "(min-width: 480px) AND (min-height: 320px)"
);

Et voilà !

Welcome to the official home of PlayStation on YouTube.

Here you'll find the latest videos about your favorite PlayStation products direct from Sony Computer Entertainment, as well as consumer generated clips from other PlayStation fans. To stay up to date, be sure to subscribe above and check back often for the new game trailers, behind-the-scenes footage, in-game moments, tips and tricks and exclusive sneak peeks.

Conclusion : Et nous voilà aligné avec les Media Queries que ce soit dans les fichiers CSS ou dans les fichiers JavaScript !

Si vous souhaitez obtenir le code final complet, c'est par ici !