Interactions

Ajoutez des comportements interactifs à n'importe quel élément HTML via un simple attribut data-interact, sans écrire une seule ligne de JavaScript.

Zéro JS à écrire. Le système d'interactions est entièrement déclaratif : vous décrivez le comportement souhaité dans le HTML, et le framework s'occupe du reste. Chargé automatiquement via loader.js, fichiers core/js/interactions.js + core/css/interactions.css.

Vue d'ensemble

Le système d'interactions permet d'ajouter des comportements interactifs à n'importe quel élément HTML via un simple attribut data-interact, sans écrire une seule ligne de JavaScript.

Exemples de ce que vous pouvez faire :

  • Afficher/masquer un élément au clic
  • Ajouter une classe au survol
  • Déclencher une action quand un élément entre dans le viewport
  • Stocker une valeur dans le localStorage
  • Appeler une fonction JavaScript globale
  • Combiner plusieurs interactions sur un même élément

Syntaxe

Format général :

data-interact="trigger | action[:params] | cible [| timing] [| once]"

Les segments sont séparés par | (pipe entouré d'espaces).

SegmentObligatoireDescriptionExemple
triggerOuiL'événement déclencheurclick, hover, viewport-enter
action[:params]OuiL'action à exécuter, avec paramètres optionnels séparés par :toggle-class:active, show
cibleNonSélecteur CSS de la cible (self par défaut)self, #menu, .panel
timingNonMoment d'exécution par rapport aux animations frameworkbefore, during, after
onceNonExécuter une seule foisonce

Exemple complet :

<button data-interact="click | toggle-class:open | #sidebar">
  Menu
</button>

Déclencheurs

10 déclencheurs disponibles :

TriggerDescriptionExemple
click Au clic sur l'élément click | toggle-class:active | self
hover Au survol (mouseenter + mouseleave avec auto‑reverse) hover | add-class:highlight | self
focus Au focus (focus + blur avec auto‑reverse) focus | add-class:focused | self
mouseenter Quand la souris entre dans l'élément (sans reverse) mouseenter | show | .tooltip
mouseleave Quand la souris quitte l'élément (sans reverse) mouseleave | hide | .tooltip
viewport-enter Quand l'élément entre dans le viewport (IntersectionObserver, seuil 10 %) viewport-enter | add-class:visible | self
viewport-exit Quand l'élément sort du viewport (après y être entré au moins une fois) viewport-exit | remove-class:visible | self
scroll Au scroll de la page (événement passif sur window) scroll | add-class:scrolled | .header
load Immédiatement au chargement (DOMContentLoaded) load | show | .welcome
window-leave Quand le curseur quitte la fenêtre par le haut (intent de fermeture) window-leave | show | .exit-popup

Actions

11 actions disponibles :

ActionSyntaxeDescriptionCible requise
show show Affiche la cible (retire l'attribut data-interact-hidden) Optionnelle
hide hide Masque la cible (ajoute data-interact-hidden) Optionnelle
toggle-class toggle-class:nom Ajoute ou retire la classe CSS Optionnelle
add-class add-class:nom Ajoute la classe CSS Optionnelle
remove-class remove-class:nom Retire la classe CSS Optionnelle
set-attr set-attr:nom:valeur Définit un attribut HTML (valeur optionnelle) Optionnelle
remove-attr remove-attr:nom Supprime un attribut HTML Optionnelle
toggle-attr toggle-attr:nom:valeur Ajoute ou supprime un attribut HTML Optionnelle
call call:nomFonction:arg1:arg2 Appelle une fonction globale (window.nomFonction) Non
storage-set storage-set:type:clé:valeur Stocke une valeur (local, session ou window) Non
storage-remove storage-remove:type:clé Supprime une valeur stockée Non

Cible

Le segment cible détermine sur quel(s) élément(s) l'action s'applique :

  • self (par défaut) — l'élément déclencheur lui‑même
  • N'importe quel sélecteur CSS — un ou plusieurs autres éléments
<!-- self : l'action s'applique au bouton lui-même -->
<button data-interact="click | toggle-class:active">Toggle</button>

<!-- Sélecteur CSS : l'action s'applique à #panel -->
<button data-interact="click | toggle-class:open | #panel">Ouvrir</button>
<div id="panel">Contenu du panneau</div>

<!-- Sélecteur multiple : tous les .card reçoivent la classe -->
<button data-interact="click | add-class:highlight | .card">Tout surligner</button>

Auto‑reverse (hover / focus)

Les triggers hover et focus inversent automatiquement l'action quand l'événement se termine (mouseleave / blur). Pas besoin d'écrire deux interactions :

ActionReverse automatique
showhide
hideshow
add-classremove-class
remove-classadd-class
set-attrremove-attr
remove-attrset-attr
toggle-classtoggle-class
toggle-attrtoggle-attr
<!-- Au survol : ajoute .highlight, au départ : la retire automatiquement -->
<div data-interact="hover | add-class:highlight">Survolez-moi</div>

<!-- Au focus : affiche le tooltip, au blur : le masque automatiquement -->
<input data-interact="focus | show | .tooltip" placeholder="Cliquez ici">
<span class="tooltip" data-interact-hidden>Aide contextuelle</span>

Timing animation

Quand l'élément déclencheur possède une classe d'animation framework (anim-*), vous pouvez contrôler le moment d'exécution de l'interaction par rapport à cette animation :

TimingComportement
beforeL'action s'exécute avant que l'animation ne se joue
during (défaut)L'action s'exécute en même temps que l'animation
afterL'action s'exécute après la fin de l'animation (animationend / transitionend)
<!-- L'action s'exécute après la fin de l'animation fade-in -->
<div class="anim-fade-in"
     data-interact="viewport-enter | add-class:loaded | self | after">
  Contenu animé
</div>

<!-- L'action s'exécute avant l'animation -->
<button class="anim-scale-in"
        data-interact="click | toggle-class:active | #panel | before">
  Action
</button>

Si l'élément n'a pas de classe anim-*, le timing est ignoré et l'action s'exécute immédiatement.

Option once

Ajoutez once à la fin pour n'exécuter l'interaction qu'une seule fois :

<!-- Le popup ne s'affiche qu'une seule fois -->
<div data-interact="window-leave | show | .exit-popup | once"></div>

<!-- La classe n'est ajoutée qu'au premier scroll -->
<header data-interact="scroll | add-class:scrolled | self | once"></header>

<!-- Combiné avec timing -->
<div class="anim-fade-in"
     data-interact="viewport-enter | add-class:revealed | self | after | once">
  Contenu
</div>

Interactions multiples

Pour attacher plusieurs interactions à un même élément, utilisez les attributs numérotés data-interact-1, data-interact-2, … jusqu'à data-interact-10 :

<button
  data-interact="click | toggle-class:open | #menu"
  data-interact-1="click | toggle-class:active | self"
  data-interact-2="click | set-attr:aria-expanded:true | self">
  Menu
</button>

L'attribut principal data-interact et les attributs numérotés fonctionnent ensemble. Tous sont exécutés.

Limite : 10 interactions numérotées maximum par élément (data-interact-1 à data-interact-10), plus l'attribut principal data-interact.

Stockage navigateur

Les actions storage-set et storage-remove permettent de lire/écrire dans le stockage du navigateur :

TypePersistancePortée
localPersistant (survit à la fermeture du navigateur)Tous les onglets du même domaine
sessionSession (disparaît à la fermeture de l'onglet)Onglet courant uniquement
windowNon persistant (mémoire JS uniquement)Onglet courant, perdu au rechargement
<!-- Stocker un choix dans le localStorage -->
<button data-interact="click | storage-set:local:theme:dark">
  Mode sombre
</button>

<!-- Supprimer la valeur -->
<button data-interact="click | storage-remove:local:theme">
  Réinitialiser
</button>

<!-- Stocker en session uniquement -->
<button data-interact="click | storage-set:session:sidebar:collapsed">
  Fermer la sidebar
</button>

<!-- Stockage window (onglet courant, non persistant) -->
<button data-interact="click | storage-set:window:step:2">
  Étape suivante
</button>

API JavaScript

FonctionDescription
window.initInteractions(root)Initialise les interactions dans un conteneur. Appelé automatiquement au chargement. Passez un élément DOM pour cibler un sous-arbre.

Utilisez cette fonction après avoir injecté du contenu dynamique (AJAX, innerHTML) :

// Après injection de HTML dynamique
document.getElementById('container').innerHTML = newHTML;
window.initInteractions(document.getElementById('container'));
Idempotent. Les éléments déjà initialisés ne sont pas re‑traités — vous pouvez appeler initInteractions() plusieurs fois sans risque de doublons.

Exemples complets

Toggle afficher/masquer

Ce panneau s'affiche et se masque au clic.

<button data-interact="click | toggle-class:open | #panel">Afficher / Masquer</button>
<div id="panel" data-interact-hidden>Contenu du panneau</div>

Classe au survol

Survolez‑moi pour ajouter la classe .highlight

<div data-interact="hover | add-class:highlight">
  Survolez-moi
</div>

Stocker une préférence

<button
  data-interact="click | storage-set:session:pref:active"
  data-interact-1="click | add-class:btn--primary | self">
  Activer
</button>

<button
  data-interact="click | storage-remove:session:pref">
  Réinitialiser
</button>

Exit intent popup

Déplacez votre curseur en dehors de la fenêtre par le haut pour déclencher le popup.

Attendez ! Ne partez pas sans découvrir nos offres.

<div data-interact="window-leave | show | #exit-popup | once"></div>

<div id="exit-popup" data-interact-hidden>
  <p>Attendez ! Ne partez pas sans découvrir nos offres.</p>
  <button data-interact="click | hide | #exit-popup">Fermer</button>
</div>

Action au scroll (une seule fois)

Scrollez la page pour ajouter une classe à cet élément.

<div data-interact="scroll | add-class:scrolled | .header | once"></div>

Interactions multiples

Ce bouton change de style, met à jour aria-pressed, et stocke une valeur en session.

<button
  data-interact="click | toggle-class:btn--primary | self"
  data-interact-1="click | toggle-attr:aria-pressed:true | self"
  data-interact-2="click | storage-set:session:state:toggled">
  Multi-actions
</button>

Tabs manuels

<button data-interact="click | add-class:active | self"
        data-interact-1="click | show | #tab-1"
        data-interact-2="click | hide | #tab-2">
  Onglet 1
</button>
<div id="tab-1">Contenu onglet 1</div>
<div id="tab-2" data-interact-hidden>Contenu onglet 2</div>

Compteur de visites (localStorage)

<section data-interact="load | storage-set:local:visited:true">
  <!-- Enregistre la visite au chargement -->
</section>

Animation + interaction séquencée

<div class="anim-fade-in-up"
  data-interact="viewport-enter | add-class:highlight | self | after | once">
  Ce bloc reçoit .highlight APRÈS son animation d'entrée
</div>

Problèmes courants

ProblèmeCause probableSolution
L'interaction ne se déclenche pas Le fichier interactions.js n'est pas chargé, ou l'attribut contient une erreur de syntaxe Vérifier que interactions.js est bien inclus (via loader.js ou manuellement). Vérifier les pipes | avec espaces.
La cible n'est pas trouvée Le sélecteur CSS est invalide ou l'élément n'existe pas encore dans le DOM Vérifier le sélecteur dans la console. Si le contenu est dynamique, appeler initInteractions() après l'injection.
show/hide ne fonctionne pas L'élément n'a pas l'attribut data-interact-hidden initial Pour masquer un élément par défaut, ajouter data-interact-hidden sur l'élément cible dans le HTML.
call affiche « Fonction non trouvée » La fonction n'est pas globale (window.nomFonction) Déclarer la fonction sur window : window.maFonction = function() { ... }
Le timing after ne se déclenche pas L'élément n'a pas de classe anim-* du framework Le timing ne fonctionne qu'avec les animations du framework. Sans anim-*, l'action s'exécute immédiatement.
L'auto-reverse ne fonctionne pas Le déclencheur ne supporte pas l'auto-reverse Seuls hover et focus supportent l'auto-reverse. Pour mouseenter/mouseleave, créer deux interactions distinctes.
L'interaction se joue plusieurs fois L'option once n'est pas activée Ajouter once en dernier segment. Pour viewport-enter, sans once l'action se rejoue à chaque entrée dans le viewport.

Référence rapide

Déclencheurs

TriggerÉvénement natifAuto‑reverse
clickclickNon
hovermouseenter / mouseleaveOui
focusfocus / blurOui
mouseentermouseenterNon
mouseleavemouseleaveNon
viewport-enterIntersectionObserver (entrée)Non
viewport-exitIntersectionObserver (sortie)Non
scrollscroll (passif)Non
loadDOMContentLoadedNon
window-leavemouseleave sur <html>Non

Actions

ActionParamètresReversible
showOui (hide)
hideOui (show)
toggle-class:classNameOui (toggle)
add-class:classNameOui (remove-class)
remove-class:classNameOui (add-class)
set-attr:name:valueOui (remove-attr)
remove-attr:nameOui (set-attr)
toggle-attr:name:valueOui (toggle)
call:fn:arg1:arg2…Non
storage-set:type:key:valueNon
storage-remove:type:keyNon

Sécurité

Le système d'interactions inclut un modèle de sécurité complet pour prévenir les attaques XSS et les abus, notamment quand les interactions sont utilisées dans du contenu généré par les utilisateurs (wireframes, CMS, templates éditables).

Fonctions bloquées (BLOCKED_FN)

L'action call valide le nom de la fonction contre une liste noire. Les fonctions suivantes sont interdites :

eval, Function, setTimeout, setInterval, requestAnimationFrame,
importScripts, fetch, XMLHttpRequest, WebSocket,
Worker, SharedWorker, ServiceWorker, import, require,
__proto__, constructor, prototype

Toute tentative d'appel à ces fonctions est silencieusement ignorée.

Attributs bloqués (BLOCKED_ATTR_RE)

Les actions set-attr et toggle-attr rejettent certains noms d'attributs dangereux :

  • on* — tous les gestionnaires d'événements (onclick, onerror, onload, etc.)
  • srcdoc — injection HTML dans les iframes
  • formaction — redirection de formulaire
  • action — cible de soumission de formulaire
  • xlink:href — liens SVG
  • data: — protocole data inline
  • javascript: — exécution JavaScript inline

Valeurs d'attributs dangereuses (DANGEROUS_ATTR_VALUE_RE)

Même si le nom de l'attribut est autorisé, les valeurs sont également vérifiées. Les valeurs contenant les patterns suivants sont bloquées :

javascript:
data:text/html
data:image/svg
vbscript:

Validations par regex

Chaque type de paramètre est validé par une regex stricte :

ParamètreCaractères autorisésExemple
Nom de classe CSS[a-zA-Z0-9_-] uniquementactive, is-open, btn_primary
Nom de fonction[a-zA-Z0-9_.$] uniquementmyApp.toggle, utils$init
Clé de stockage[a-zA-Z0-9_.-], max 100 caractèrestheme, sidebar.state, app_v2

Les espaces et caractères spéciaux sont rejetés pour empêcher les injections.

Limites

LimiteValeurDescription
MAX_TARGETS100Nombre maximum d'éléments ciblés par une seule interaction
Longueur de règle500 caractèresTaille maximum d'une valeur data-interact
Sélecteur invalidetry/catchquerySelectorAll est encapsulé dans un try/catch pour éviter les erreurs sur un sélecteur malformé
Pourquoi ces protections ? Ces mécanismes de sécurité sont essentiels quand les interactions sont utilisées dans du contenu généré par les utilisateurs — wireframes partagés, sections CMS, templates éditables. Sans ces protections, un attribut data-interact malicieux pourrait exécuter du code arbitraire, voler des données ou rediriger l'utilisateur.

Voir aussi