You might not need jQuery

C’est une référence directe à ce site que je consulte régulièrement quand il me vient l’idée de remplacer jQuery par du javascript natif dans le code.

À l’occasion de la création d’un nouveau blog — de l’autre côté de la mare, comme ils disent là-bas — que je suis depuis quelques jours, je me suis rendu compte qu’il avait conservé le thème Berlin pour l’apparence, sans activer le chargement de jQuery, et forcément, Berlin utilise pour animer deux trois bricoles, un peu beaucoup de jQuery.

C’était donc l’occasion de mettre les mains dans le cambouis et de basculer côté natif pour s’affranchir du chargement de cette bibliothèque dans Berlin et Ductile. Ça n’empêchera pas par ailleurs de demander le chargement de jQuery si un de vos plugins l’utilise côté public ; je ne parle que des scripts spécifiques aux deux thèmes.

J’ai fait ça hier et voilà ce que ça donne pour Berlin où on passe de ceci :

/*global $, dotclear */
'use strict';

const dotclear_berlin = dotclear.getData('dotclear_berlin');

$('html').addClass('js');
// Show/Hide main menu
$('.header__nav')
  .before(`<button id="hamburger" type="button" aria-label="${dotclear_berlin.navigation}" aria-expanded="false"></button>`)
  .toggle();
$('#hamburger').on('click', function () {
  $(this).attr('aria-expanded', $(this).attr('aria-expanded') == 'true' ? 'false' : 'true');
  $(this).toggleClass('open');
  $('.header__nav').toggle('easing', () => {
    if ($('#hamburger').hasClass('open')) {
      $('.header__nav li:first a')[0].focus();
    }
  });
});
// Show/Hide sidebar on small screens
$('#main').prepend(
  `<button id="offcanvas-on" type="button"><span class="visually-hidden">${dotclear_berlin.show_menu}</span></button>`,
);
$('#offcanvas-on').on('click', () => {
  const btn = $(
    `<button id="offcanvas-off" type="button"><span class="visually-hidden">${dotclear_berlin.hide_menu}</span></button>`,
  );
  $('#wrapper').addClass('off-canvas');
  $('#footer').addClass('off-canvas');
  $('#sidebar').prepend(btn);
  btn[0].focus({
    preventScroll: true,
  });
  btn.on('click', (evt) => {
    $('#wrapper').removeClass('off-canvas');
    $('#footer').removeClass('off-canvas');
    evt.target.remove();
    $('#offcanvas-on')[0].focus();
  });
});
$(document).ready(() => {
  // totop init
  const $btn = $('#gotop');
  const $link = $('#gotop a');
  $link.attr('title', $link.text());
  $link.html(
    '<svg width="24px" height="24px" viewBox="1 -6 524 524" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M460 321L426 355 262 192 98 355 64 321 262 125 460 321Z"></path></svg>',
  );
  $btn.css({
    width: '32px',
    height: '32px',
    padding: '3px 0',
  });
  // totop scroll
  $(window).scroll(function () {
    if ($(this).scrollTop() == 0) {
      $btn.fadeOut();
    } else {
      $btn.fadeIn();
    }
  });
  $btn.on('click', (e) => {
    $('body,html').animate(
      {
        scrollTop: 0,
      },
      800,
    );
    e.preventDefault();
  });
  // scroll comment preview if present
  document.getElementById('pr')?.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
});

à cela :

/*global dotclear */
'use strict';

const dotclear_berlin = dotclear.getData('dotclear_berlin');

// Button templates
dotclear_berlin.template = {
  hamburger: `<button id="hamburger" type="button" aria-label="${dotclear_berlin.navigation}" aria-expanded="false"></button>`,
  offcanvas: {
    on: `<button id="offcanvas-on" type="button"><span class="visually-hidden">${dotclear_berlin.show_menu}</span></button>`,
    off: `<button id="offcanvas-off" type="button"><span class="visually-hidden">${dotclear_berlin.hide_menu}</span></button>`,
  },
};

document.querySelector('html').classList.add('js');

{
  // Show/Hide main menu
  const header_nav = document.querySelector('.header__nav');
  const hamburger = new DOMParser().parseFromString(dotclear_berlin.template.hamburger, 'text/html').body.firstElementChild;
  header_nav.insertAdjacentElement('beforebegin', hamburger);
  header_nav.classList.add('hide');

  // Show/Hide sidebar on small screens
  const main = document.getElementById('main');
  const offcanvas = new DOMParser().parseFromString(dotclear_berlin.template.offcanvas.on, 'text/html').body.firstElementChild;
  main.insertBefore(offcanvas, main.firstChild);
}

document.addEventListener('DOMContentLoaded', () => {
  // Show/Hide main menu
  const header_nav = document.querySelector('.header__nav');
  const hamburger = document.getElementById('hamburger');
  hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('open');
    if (hamburger.classList.contains('open')) {
      hamburger.setAttribute('aria-expanded', 'true');
      header_nav.classList.add('show');
      header_nav.classList.remove('hide');
      document.querySelector('.header__nav li.li-first a').focus();
      return;
    }
    hamburger.setAttribute('aria-expanded', 'false');
    header_nav.classList.add('hide');
    header_nav.classList.remove('show');
  });

  // Show/Hide sidebar on small screens
  const offcanvas = document.getElementById('offcanvas-on');
  offcanvas.addEventListener('click', () => {
    const sidebar = document.getElementById('sidebar');
    const wrapper = document.getElementById('wrapper');
    const footer = document.getElementById('footer');
    const button = new DOMParser().parseFromString(dotclear_berlin.template.offcanvas.off, 'text/html').body.firstElementChild;
    wrapper.classList.add('off-canvas');
    footer.classList.add('off-canvas');
    sidebar.insertBefore(button, sidebar.firstChild);
    button.focus({
      preventScroll: true,
    });
    button.addEventListener('click', (evt) => {
      wrapper.classList.remove('off-canvas');
      footer.classList.remove('off-canvas');
      evt.target.remove();
      offcanvas.focus();
    });
  });

  // totop init
  const gotop_btn = document.getElementById('gotop');
  const gotop_link = document.querySelector('#gotop a');
  gotop_link.setAttribute('title', gotop_link.textContent);
  gotop_link.innerHTML =
    '<svg width="24px" height="24px" viewBox="1 -6 524 524" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M460 321L426 355 262 192 98 355 64 321 262 125 460 321Z"></path></svg>';
  gotop_btn.style.width = '32px';
  gotop_btn.style.height = '32px';
  gotop_btn.style.padding = '3px 0';

  // totop scroll
  window.addEventListener('scroll', () => {
    if (document.querySelector('html').scrollTop === 0) {
      gotop_btn.classList.add('hide');
      gotop_btn.classList.remove('show');
    } else {
      gotop_btn.classList.add('show');
      gotop_btn.classList.remove('hide');
    }
  });
  gotop.addEventListener('click', (e) => {
    function scrollTo(element, to, duration) {
      const easeInOutQuad = (time, start, change, duration) => {
        time /= duration / 2;
        if (time < 1) return (change / 2) * time * time + start;
        time--;
        return (-change / 2) * (time * (time - 2) - 1) + start;
      };
      let currentTime = 0;
      const start = element.scrollTop;
      const change = to - start;
      const increment = 20;
      const animateScroll = () => {
        currentTime += increment;
        element.scrollTop = easeInOutQuad(currentTime, start, change, duration);
        if (currentTime < duration) {
          setTimeout(animateScroll, increment);
        }
      };
      animateScroll();
    }
    scrollTo(document.querySelector('html'), 0, 800);
    e.preventDefault();
  });

  // scroll comment preview if present
  document.getElementById('pr')?.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
});

Quant à Ductile, on passe de ceci :

/*global $ */
'use strict';

$(document).ready(() => {
  if ($(window).width() < 1024) {
    const create_name = (text) =>
      text
        .toLowerCase()
        // Remove leading and trailing spaces, and any non-alphanumeric
        // characters except for ampersands, spaces and dashes.
        .replace(/^\s+|\s+$|[^a-z0-9&\s-]/g, '')
        // Replace '&' with 'and'.
        .replace(/&/g, 'and')
        // Replaces spaces with dashes.
        .replace(/\s/g, '-')
        // Squash any duplicate dashes.
        .replace(/(-)+\1/g, '$1');

    // Set toggle class to each #sidebar h2
    $('#sidebar div div h2').addClass('toggle');

    // Hide all h2.toggle siblings
    $('#sidebar div div h2').nextAll().hide();

    // Add a link to each h2.toggle element.
    $('h2.toggle').each(function () {
      // Convert the h2 element text into a value that
      // is safe to use in a name attribute.
      const name = create_name($(this).text());

      // Create a name attribute in the following sibling
      // to act as a fragment anchor.
      $(this).next().attr('name', name);

      // Replace the h2.toggle element with a link to the
      // fragment anchor.  Use the h2 text to create the
      // link title attribute.
      $(this).html(`<a href="#${name}" title="Reveal ${$(this).text()} content">${$(this).html()}</a>`);
    });

    // Add a click event handler to all h2.toggle elements.
    $('h2.toggle').on('click', function (event) {
      event.preventDefault();
      // Toggle the 'expanded' class of the h2.toggle
      // element, then apply the slideToggle effect
      // to all siblings.
      $(this).toggleClass('expanded').nextAll().slideToggle('fast');
    });

    // Remove the focus from the link tag when accessed with a mouse.
    $('h2.toggle a').on('mouseup', function () {
      // Use the blur() method to remove focus.
      $(this).trigger('blur');
    });
  }
});

à cela :

'use strict';

document.addEventListener('DOMContentLoaded', () => {
  // Show/Hide main menu
  if (document.body.clientWidth < 1024) {
    const create_name = (text) =>
      text
        .toLowerCase()
        // Remove leading and trailing spaces, and any non-alphanumeric
        // characters except for ampersands, spaces and dashes.
        .replace(/^\s+|\s+$|[^a-z0-9&\s-]/g, '')
        // Replace '&' with 'and'.
        .replace(/&/g, 'and')
        // Replaces spaces with dashes.
        .replace(/\s/g, '-')
        // Squash any duplicate dashes.
        .replace(/(-)+\1/g, '$1');

    // Set toggle class to each #sidebar h2
    const h2 = document.querySelectorAll('#sidebar div div h2');
    h2.forEach((element) => {
      element.classList.add('toggle');
      element.parentNode.classList.add('hide');
      const name = create_name(element.textContent);
      element.nextElementSibling.setAttribute('name', name);
      element.innerHTML = `<a href="#${name}" title="Reveal ${element.textContent} content">${element.innerHTML}</a>`;
      element.addEventListener('click', (e) => {
        e.preventDefault();
        element.parentNode.classList.toggle('hide');
      });
    });

    // Remove the focus from the link tag when accessed with a mouse.
    const h2_link = document.querySelectorAll('h2.toggle a');
    h2_link.forEach((element) => {
      element.addEventListener('mouseup', () => {
        const event = new Event('blur', { bubbles: true, cancelable: false });
        element.dispatchEvent(event);
      });
    });
  }
});

Pour être tout à fait honnête j’ai ajouté quelques règles CSS pour gérer les transitions de style et les visibilités de quelques éléments, la plupart étant auparavant prises en charge par jQuery.

Pour Berlin, j’ai ajouté ceci :

.header__nav.show {
    opacity: 1;
    height: auto;
    transition: all 1s ease;
}
.header__nav.hide {
    overflow: hidden;
    opacity: 0;
    height: 0;
}
#gotop.show {
    display: block;
    opacity: 1;
    transition: opacity 400ms;
}
#gotop.hide {
    opacity: 0;
    transition: opacity 400ms;
}

Et pour Ductile, cela :

#sidebar div.hide *:not(h2.toggle):not(h2.toggle *) {
  display: none;
}

Ce qui permet, si on utilise aucun plugin dépendant de jQuery, environ 150 kilo-octets en moins à charger pour les visiteurs, toujours ça gagné !

Ajouter un commentaire

Les champs suivis d'un * sont obligatoires

Les commentaires peuvent être formatés en utilisant la syntaxe Markdown Extra.

Ajouter un rétrolien

URL de rétrolien : https://open-time.net/trackback/15266

Haut de page