Aller au contenu

JavaScript⚓︎

Raccourci pour sélectionner une liste de nœuds⚓︎

🐣 2022-08

La méthode standard qui consiste à utiliser querySelectorAll() est relativement verbeuse et ne permet pas de faire facilement un forEach() dessus. Cette version simplifiée est un raccourci utile lorsqu’on doit le faire souvent :

qsa.js
1
2
3
function qsa(selector, root = document) {
  return Array.from(root.querySelectorAll(selector)) // (1)!
}
  1. On en profite pour retourner un Array !

Récupérer la chaîne de l’ancre d’une URL⚓︎

🐣 2022-08

Je passe toujours trop de temps à retrouver comment faire.

anchor.js
1
2
3
function currentAnchor() {
  return document.location.hash ? document.location.hash.slice(1) : ''
}

Prendre en compte les préférences utilisateur·ice pour lancer une animation⚓︎

🐣 2022-08

Très important car des personnes sont mal à l’aise avec certaines animations (j’en fait partie, parfois).

motion.js
1
2
3
4
5
6
7
const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches

if (prefersReducedMotion !== true) {
  // do animation
}

Voir aussi ce qu’il est possible de faire en HTML et en CSS.

Raccourci pour créer/attacher un évènement⚓︎

🐣 2022-08

Un bout de code qui vient de Chris Ferdinandi (voir 1, 2 et 3) :

emit.js
1
2
3
4
5
6
7
8
function emit(type, detail, elem = document) {
  let event = new CustomEvent(type, {
    bubbles: true,
    cancelable: true,
    detail: detail,
  })
  return elem.dispatchEvent(event)
}

Il peut s’utiliser ainsi emit('calculator:add', {total: 20}, calendarElement) et s’écouter de manière classique.

Polyfills à la demande⚓︎

🐣 2022-08

Il est possible d’importer des polyfills externes directement depuis une balise <script>, par exemple pour Promise ou fetch :

polyfills.html
<script>
  window.Promise ||
    document.write(
      '<script src="https://unpkg.com/es6-promise@4.2.8/dist/es6-promise.auto.min.js"><\/script>'
    )
  window.fetch ||
    document.write(
      '<script src="https://unpkg.com/whatwg-fetch@3.6.2/dist/fetch.umd.js"><\/script>'
    )
</script>

En pratique, c’est quand même mieux de ne pas faire des requêtes externes et d’avoir les fichiers en local.

Copier dans le presse-papier⚓︎

🐣 2022-08

Lorsqu’il y a un champ avec un code ou une URL à copier-coller, ça peut être intéressant de proposer à l’utilisateur·ice de cliquer pour mettre l’information dans son presse-papier et le coller ailleurs.

copy-clipboard.js
function copyToClipboard(codeElement, alert) {
  try {
    navigator.clipboard.writeText(codeElement.innerText)
    alert.innerHTML = `<div class="custom-class">Code copied!</div>`
    setTimeout(() => {
      alert.innerHTML = ''
    }, 3000) // (1)
  } catch (ex) {
    alert.innerHTML = ''
  }
}
  1. Vous pouvez adapter cette valeur qui correspond au temps d’affichage de l’information (en millisecondes).

Confirmation de suppression⚓︎

🐣 2022-08

La façon la plus simple d’ouvrir une popup de confirmation.

delete-confirmation.js
1
2
3
4
5
6
7
function deleteConfirmation(event) {
  if (window.confirm('Are you sure you want to delete this?')) {
    return
  } else {
    event.preventDefault()
  }
}

Fermeture automatique

Notez qu’à force de recevoir des notifications, les utilisateur·ices sont promptes à fermer une fenêtre, aussi rouge soit-elle. Il peut-être pertinent de demander une confirmation qui demande de saisir quelque chose comme le fait Github lorsqu’on supprime un dépôt (il faut taper le nom complet du dépôt concerné).

Générer un slug⚓︎

🐣 2022-08

Pour transformer un titre en français en une portion d’URL par exemple. Adapté depuis cet article.

slugify.js
function slugify(str) {
  const a =
    'àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·/_,:;'
  const b =
    'aaaaaaaaaacccddeeeeeeeegghiiiiiilmnnnnoooooooooprrsssssttuuuuuuuuuwxyyzzz------'
  const p = new RegExp(a.split('').join('|'), 'g')

  return str
    .toString()
    .toLowerCase()
    .replace(/\s+/g, '-') // Replace spaces with -
    .replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
    .replace(/’/g, "'") // Turn apostrophes to single quotes
    .replace(/[^a-zA-Z0-9-']+/g, '') // Remove all non-word characters except single quotes
    .replace(/--+/g, '-') // Replace multiple - with single -
    .replace(/^-+/, '') // Trim - from start of text
    .replace(/-+$/, '') // Trim - from end of text
}

Un système de plugins⚓︎

🐣 2022-08

Ce fragment est issu de ce qu’à fait Simon Willison pour Datasette, aidé par Matthew Somerville.

plugins.js
var datasette = datasette || {}
datasette.plugins = (() => {
  var registry = {}
  return {
    register: (hook, fn) => {
      registry[hook] = registry[hook] || []
      registry[hook].push(fn)
    },
    call: (hook, args) => {
      var results = (registry[hook] || []).map((fn) => fn(args || {}))
      return results
    },
  }
})()

Il s’utilise ensuite ainsi :

1
2
3
datasette.plugins.register('numbers', ({a, b}) => a + b)
datasette.plugins.register('numbers', o => o.a * o.b)
datasette.plugins.call('numbers', {a: 4, b: 6})

Paramètres par défaut⚓︎

🐣 2022-08

Il est possible de définir des paramètres par défaut en utilisant la syntaxe suivante :

parameters.js
1
2
3
function checkName({ name = 'Castor' } = {}) {
  console.log({ name })
}

Il est aussi possible d’avoir un comportement similaires aux **kwargs en Python grâce au rest parameter :

parameters-kwargs.js
1
2
3
function checkNameWithKwargs({ name = 'Pollux', ...kwargs } = {}) {
  console.log({ name, ...kwargs })
}

Cela nécessite d’appeler la fonction avec un objet (ce qui pourrait être une bonne pratique ?).

checkNameWithKwargs({name: 'Télaïre', tessiture:'soprano'})

Télécharger un fichier dynamiquement⚓︎

🐣 2022-09

Parfois, on veut pouvoir télécharger un fichier généré dynamiquement en JavaScript, par exemple ici pour transformer un tableau en un export CSV (la récupération des données n’est pas montrée).

download-file.js
function downloadAsCSV(event) {
  event.preventDefault()
  const content = 'foo;bar' // (1)!
  const name = 'baz_quux'
  const filename = `export_${new Date().toLocaleDateString()}_${name}.csv` // (2)!
  const csvFile = new Blob([content], { type: 'text/csv' }) // (3)!
  const fakeDownloadLink = document.createElement('a') // (4)!
  fakeDownloadLink.download = filename // (5)
  fakeDownloadLink.href = window.URL.createObjectURL(csvFile) // (6)!
  fakeDownloadLink.style.display = 'none'
  document.body.appendChild(fakeDownloadLink)
  fakeDownloadLink.click() // (7)!
  fakeDownloadLink.remove()
}
  1. Le contenu souhaité du fichier à aller récupérer par ailleurs
  2. C’est toujours intéressant d’avoir la date dans des fichiers d’export
  3. Création d’un Blob simulant le fichier
  4. Création du faux lien qui va nous servir à simuler le téléchargement
  5. Utilisation de l’attribut download qui permet de donner un nom au fichier téléchargé
  6. Création d’une URL contenant le contenu du fichier
  7. Simulation du clic avant la suppression de l’élément pour lancer le téléchargement

Enlever des espaces⚓︎

🐣 2022-09

Il est très courant de vouloir enlever des espaces et sauts de lignes (depuis un element.textContent par exemple).

strip-extra-spaces.js
const stripExtraSpaces = (str) => str.replace(/\r?\n|\r|\s\s+/g, '')

Avoir une méthode range()⚓︎

🐣 2022-11

Un moyen d’avoir une méthode range() qui ait le même comportement qu’en Python, décrite en détail par Kieran Barker et adaptée par Chris Ferdinandi.

range.js
1
2
3
4
5
6
7
8
9
function range(stop, start = 1, step = 1) {
  return Array.from(
    { length: (stop - start) / step + 1 },
    (_, i) => start + i * step
  )
}
// range(5) => [ 1, 2, 3, 4, 5 ]
// range(10, 8) => [ 8, 9, 10 ]
// range(10, 1, 3) => [ 1, 4, 7, 10 ]

Avoir une méthode zip()⚓︎

🐣 2022-11

Un vieux truc que j’avais récupéré sur StackOverflow qui est quasiment un équivalent de la méthode zip() en Python.

zip.js
1
2
3
4
function zip(rows) {
  return rows[0].map((_, index) => rows.map((row) => row[index]))
}
// zip([['foo', 'bar'], [1, 2]]) => [['foo', 1], ['bar', 2]]

Utiliser des loggers personnalisés⚓︎

🐣 2022-11

Il est possible de définir ses propres loggers pour s’y retrouver plus facilement dans la console. C’est notamment utile si vous faites une lib ou un module réutilisable.

logger.js
1
2
3
4
5
function logger(scope, level = 'log', color = 'green') {
  return (...args) => {
    console[level]('%c%s', `color:${color}`, scope, ...args)
  }
}

Cela peut s’utiliser ensuite ainsi (très adaptable en fonction de votre sensibilité) :

1
2
3
const log = logger('myapp')
const logerr = logger('myapp', 'error', 'red')
const logdev = logger('myapp', 'debug', 'blue')

Et puis dans votre code enfin :

1
2
3
log('page initialized')
logerr('oops')
logdev('wtf')

Je vous laisse vous amuser dans la console :).

🐫 Du camel au kebab⚓︎

🐣 2022-11

Toute petite fonction mais bien pratique pour convertir des noms de variables en data-attributes par exemple !

camel-to-kebab.js
1
2
3
function camelToKebab(s) {
  return s.replace(/[A-Z]/g, (s) => '-' + s.toLowerCase())
}

Raccourcis pour les attributs⚓︎

🐣 2022-11

Et là vous allez comprendre pourquoi la précédente fonction était utile.

attributes.js
function attr(el, name, value = undefined) {
  const curValue = el.getAttribute(name)
  if (typeof name === 'object') {
    for (const at in name) {
      el.setAttribute(camelToKebab(at), name[at]) // (1)!
    }
    return null
  } else if (value === undefined) return el.getAttribute(name)
  else if (value === null) return el.removeAttribute(name), curValue
  else return el.setAttribute(name, value), value
}
  1. Nécessite la fonction camelToKebab() définie juste au-dessus.

Cela s’utilise ainsi :

1
2
3
4
5
attr(el, 'foo') // récupère l’attribut `foo` de `el`
attr(el, 'bar', 'baz') // l’attribut `bar` passe à `baz`
attr(el, 'quux', null) // retrait de l’attribut `quux`
attr(el, [ dataFoo: 'valueBar', dataBaz: 'valueQuux' ])
// set l’attribut `data-foo` à `valueBar` et `data-baz` à `valueQuux`

Chargement lorsque le DOM est prêt⚓︎

🐣 2022-11

Il y pas mal de façon de le faire mais ça revient un peu toujours au même. J’aime bien que ce soit explicite et que ça prenne soin de retirer l’écouteur de l’évènement à la fin.

dom-ready.js
function onload() {
  // An example of what you might want to do:
  var root = document.documentElement
  root.classList.remove('no-js')

  // Do something!

  // Clean up
  document.removeEventListener('DOMContentLoaded', onload)
}

if (document.readyState != 'loading') {
  onload()
} else {
  document.addEventListener('DOMContentLoaded', onload)
}

Pour aller plus loin/différemment⚓︎


Dernière mise à jour: 2022-11-30