5566 sujets

Sémantique web et HTML

Bonjour à tous,

Je travaille actuellement sur un projet visant à afficher des extraits d’articles sous forme d’accordéon. Le système est déjà en place : il suffit de cliquer sur un article replié pour l’ouvrir, puis de cliquer à nouveau en dehors de cet article pour le refermer. La navigation entre les articles peut également se faire à l’aide des touches Tabulation et Majuscule+Tabulation.

Il reste cependant un défi à relever : les liens situés dans les sections repliées restent inactifs même lorsque l’article est déplié.

Je serais reconnaissant pour toute aide ou suggestion pour résoudre ce problème. Merci d’avance pour votre coopération.
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Test de focus</title>
  <link rel="stylesheet" href="/css/screen.css">
</head>
<body>
  <div class="colonne-principale vignettes longueur-30-lignes" role="region" aria-label="Test de focus">
    <article class="vignette lire-la-suite-pourpre" tabindex="0">
      <p>Lien&nbsp;: <a href="https://google.fr" class="" target="_blank" rel="noreferrer nofollow">Glop</a></p>
      <p>Article 1</p>
      <p>Lien&nbsp;: <a href="https://google.fr" class="" target="_blank" rel="noreferrer nofollow">Pas glop</a></p>
    </article>
    <article class="vignette lire-la-suite-pourpre" tabindex="0">
      <p>Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2  Article 2</p>
    </article>
    <article class="vignette lire-la-suite-pourpre" tabindex="0">
      <p>Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3</p>
    </article>
    <article class="vignette lire-la-suite-pourpre" tabindex="0">
      <p>Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4</p>
    </article>
  </div>
  <!-- Prise de focus des articles de proche en proche -->
  <script src="/js/fonction-article-focus-seul.js"></script>
</body>
</html>

.colonne-principale {
  display: block;
  justify-content: space-between;
}

.colonne-principale.vignettes {
  display: grid;
  grid-template-columns: repeat(3), 1fr);
  gap: var(--gouttiere);
}

.colonne-principale.vignettes article.vignette {
  position: relative; /* Imposer l’article en position relative */
}

.longueur-30-lignes .vignette {max-height: calc(1.500em * 3);}
.longueur-40-lignes .vignette {max-height: calc(1.500em * 4);}

.longueur-30-lignes .vignette,
.longueur-40-lignes .vignette {
  overflow: hidden;
  transition: max-height 0.2s ease;
  text-align: left;
}

.longueur-30-lignes .vignette::after,
.longueur-40-lignes .vignette::after {
  content: "Lire la suite ?";
  display: block;
  width: calc(100% - 2 * 1);
  height: calc(1.500em + 1);
  background-color: red;
  position: absolute;
  bottom: 0;
  left: 1;
  text-align: left;
  border-top: var(--lisere) solid rgba(black, 1);
  box-sizing: border-box;
}

.longueur-30-lignes .vignette.depliage,
.longueur-40-lignes .vignette.depliage {
  max-height: 100%;
}

.longueur-30-lignes .vignette.depliage::after,
.longueur-40-lignes .vignette.depliage::after {
  display: none;
}

document.addEventListener("DOMContentLoaded", function() {
  const articles = document.querySelectorAll(".vignette");

  // Fermer tous les articles
  function fermerTousLesArticles() {
    articles.forEach(article => article.classList.remove("depliage"));
  }

  // Ouvrir un article sans activer les focus internes
  function ouvrirArticle(article) {
    article.classList.add("depliage");
  }

  // Initialiser les articles pour désactiver le focus interne
  articles.forEach(article => {
    const focusableElements = article.querySelectorAll("a, button, input, textarea, select, [tabindex]");
    focusableElements.forEach(element => element.setAttribute("tabindex", "-1"));
  });

  // Ajouter un gestionnaire de clic pour chaque article
  articles.forEach(article => {
    article.addEventListener("click", function(event) {
      // Empêcher la propagation de l’événement de clic à l’élément parent
      event.stopPropagation();
      // Fermer tous les articles
      fermerTousLesArticles();
      // Ouvrir l'article cliqué
      ouvrirArticle(article);
      // Donner le focus à l'article
      article.focus();
    });

    // Ajouter un gestionnaire de focus pour chaque article
    article.addEventListener("focus", function(event) {
      // Fermer tous les articles
      fermerTousLesArticles();
      // Ouvrir l’article qui reçoit le focus
      ouvrirArticle(article);
    });

    // Ajouter un gestionnaire de blur pour chaque article
    article.addEventListener("blur", function() {
      // Fermer l’article lorsqu’il perd le focus
      article.classList.remove("depliage");
    });
  });

  // Ajouter un gestionnaire de clic pour fermer les articles en cliquant en dehors d’un article
  document.addEventListener("click", function() {
    fermerTousLesArticles();
  });
});

Modifié par Pyanepsion (16 Jul 2024 - 13:44)
Modérateur
Bonsoir,

Ton gestionnaire de clic pour chaque article , script attrape tous les click via event.stopPropagation(); , ce qui empeche d'acceder à n'importe quel element cliquable qu'il contient, liens ou éléments de formulaire ou affublé d'un tabindex.

Il faudrait ignoré le click sur le parent mais laisser les enfants accessibles au click .

Exemple en CSS en lieu et place du gestionnaire js.


.longueur-30-lignes .vignette:focus-within,
.longueur-40-lignes .vignette:focus-within {
  max-height: 100%;
  pointer-events:none;
}

.longueur-30-lignes .vignette:focus-within *,
.longueur-40-lignes .vignette:focus-within *{
  pointer-events:auto;
}


Cdt
@gcyrillus, Superbe. C’est sobre et élégant. Merci.
Je voudrais maintenant différencier deux cas de réaction aux touches Tabulation et Majuscule + Tabulation.
1. Prise de focus interne
<div class="colonne-principale vignettes longueur-40-lignes" role="region" aria-label="Avis client">

prend le focus sur les éléments cliquables internes, ici le lien dans notre exemple.

2. Pas de focus interne
<div class="colonne-principale vignettes longueur-30-lignes" role="region" aria-label="Avis client">

ne prend pas le focus sur les éléments cliquables internes, ici le lien dans notre exemple.
Modérateur
Bonjour,

Je ne comprend pas ta question, du moins CSS est incapable de différencier des touches ni de gerer des raccourcies clavier à ma connaissance.

J'ai fait un codepen avec l'idée précédente : https://codepen.io/gc-nomade/pen/jOjWWQo

Quels résultats attends tu en dehors de récupérer l'interaction au click sur les enfants.

Cdt
Le site Internet propose deux séries de billets, chacun dans un contexte bien différent de l’autre.

Page avec longueur-30-lignes :
Objectif : Ne pas attirer particulièrement l’attention sur les liens. Ces liens sont cités uniquement parce qu’ils renvoient vers un site extérieur traitant de l’article.
Comportement souhaité : La touche Tabulation doit permettre de passer directement à l’article suivant sans que le focus soit attiré sur les liens tels que « Glop » ou « Pas Glop ».

Page avec longueur-40-lignes :
Objectif : Inciter le visiteur à cliquer sur les liens, car ils proposent des articles à la vente.
Comportement souhaité : La touche Tabulation doit attirer le focus sur chaque lien (par exemple, « Glop » puis « Pas Glop »), avant de passer à l’article suivant.

La même logique s’applique pour Majuscule + Tabulation pour naviguer en sens inverse.

La solution pour la page longueur-40-lignes est bien illustrée sur Codepen. Je n’arrive pas à trouver la solution qui va avec (longueur-30-lignes).
Modérateur
Re,
Bien, du coup, la partie javascript pour le tabindex est à garder en sélectionnant les vignette depuis le parent .longueur-30-lignes

const articles = document.querySelectorAll(".longueur-30-lignes .vignette");

// Initialiser les articles pour désactiver le focus interne
articles.forEach((article) => {
  const focusableElements = article.querySelectorAll(
    "a, button, input, textarea, select, [tabindex]"
  );
  focusableElements.forEach((element) =>
    element.setAttribute("tabindex", "-1")
  );
});


maj du pen : https://codepen.io/gc-nomade/pen/jOjWWQo si j'ai compris Smiley smile
Bonjour,

a écrit :
– Comportement souhaité : La touche Tabulation doit permettre de passer directement à l’article suivant sans que le focus soit attiré sur les liens tels que « Glop » ou « Pas Glop ».


Je peux éventuellement comprendre la logique marketing voulue, mais tu rends ton site inaccessible en faisant cela.

Comment une personne handicapée moteur se servant uniquement de tab et ne pouvant pas se servir d'une souris ou d'un écran tactile va faire pour ouvrir ces liens ?

IL ne faut pas empêcher les gens d'aller voir ailleurs si tel est leur souhait. LE contraire peut donner la désagréable impression de se faire piéger ou d'avoir affaire à quelqu'un de malhonnête. Personnellement j'ai tendance à fuir les sites qui font du marketing trop agressif, qui le montrent avec toutes sortes de trucs à priori anodins de ce genre, et pour lesquels je n'ai pas d'attachement particulièrement fort à la marque, mais c'est à toi de voir. Pour moi en tout cas, c'est un gros red flag.
Modifié par QuentinC (18 Jul 2024 - 06:28)
Bien vu @QuentinC.
QuentinC a écrit :
Comment une personne handicapée moteur se servant uniquement de tab et ne pouvant pas se servir d'une souris ou d'un écran tactile va faire pour ouvrir ces liens ?

Bien qu’un handicapé moteur ne soit pas du tout la cible du site Internet pour lequel ce code est destiné, ce n’est évidemment pas une raison pour ne pas tenir compte de toutes les accessibilités.

Voici donc une modification du comportement. Je propose également une version en français de codePen pour ne pas pénaliser les non-anglophones à qui se destine ce forum, ainsi qu’une meilleure différenciation, pour une meilleure compréhension, entre le premier et le deuxième lien externe de l’exemple :
  <p tabindex="0">Sélectionnez-moi puis cliquez sur <kbd>Tabulation</kbd>.</p>
  <div class="colonne-principale vignettes longueur-40-lignes" role="region" aria-label="Test de focus">
    <article class="vignette" tabindex="0">
      <p>Lien&nbsp;: <a href="https://google.fr" class="" target="_blank" rel="noreferrer nofollow" >Focus sur la tabulation</a></p>
      <p>Article 1 Article 1 Article 1 Article 1</p>
      <p>Lien&nbsp;: <a href="https://google.com" class="" target="_blank" rel="noreferrer nofollow">Focus sur la tabulation correct</a></p>
    </article>
    <article class="vignette" tabindex="0">
      <p>Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2</p>
    </article>
    <article class="vignette" tabindex="0">
      <p>Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3</p>
    </article>
    <article class="vignette" tabindex="0">
      <p>Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4</p>
    </article>
  </div>
  <div class="colonne-principale vignettes longueur-30-lignes" role="region" aria-label="Test de focus">
    <article class="vignette" tabindex="0">
      <p>Lien&nbsp;: <a href="https://google.fr" class="" target="_blank" rel="noreferrer nofollow">Focus caché sur la tabulation</a></p>
      <p>Article 1 Article 1 Article 1 Article 1</p>
      <p>Lien&nbsp;: <a href="https://google.com" class="" target="_blank" rel="noreferrer nofollow">Pas de focus sur la tabulation</a></p>
    </article>
    <article class="vignette" tabindex="0">
      <p>Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2 Article 2</p>
    </article>
    <article class="vignette" tabindex="0">
      <p>Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3 Article 3</p>
    </article>
    <article class="vignette" tabindex="0">
      <p>Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4 Article 4</p>
    </article>
  </div>
  <p tabindex="0">Maintenant, tapez <kbd>Majuscule</kbd> + <kbd>Tabulation</kbd> remonter.</p>

et :
const articles = document.querySelectorAll(".longueur-30-lignes .vignette");

// Initialiser les articles pour désactiver le focus interne
articles.forEach((article, articleIndex) => {
  const focusableElements = article.querySelectorAll(
    "a, button, input, textarea, select, [tabindex]"
  );
  focusableElements.forEach((element) => element.setAttribute("tabindex", "-1"));

  // Ajouter un écouteur d’événements pour détecter la touche Entrée et Tabulation
  article.addEventListener("keydown", (e) => {
    if (e.key === "Enter") {
      const focusableElements = Array.from(article.querySelectorAll("a, button, input, textarea, select, [tabindex='-1'], [tabindex='0']"));
      const currentIndex = focusableElements.indexOf(document.activeElement);

      if (currentIndex === -1) {
        // Si aucun élément interne n’est en focus, donner le focus au premier élément focusable
        if (focusableElements.length > 0) {
          focusableElements[0].setAttribute("tabindex", "0");
          focusableElements[0].focus();
          e.preventDefault(); // Empêche le comportement par défaut pour éviter le défilement de la page
        }
      } else if (focusableElements[currentIndex].tagName.toLowerCase() === 'a') {
        // Si l’élément en focus est un lien, suivre le comportement normal de la touche Entrée (ouvrir le lien)
        focusableElements[currentIndex].click();
      }
    } else if (e.key === "Tab") {
      const focusableElements = Array.from(article.querySelectorAll("a, button, input, textarea, select, [tabindex='-1'], [tabindex='0']"));
      const currentIndex = focusableElements.indexOf(document.activeElement);

      if (currentIndex !== -1) {
        if (currentIndex + 1 < focusableElements.length) {
          // Donner le focus au prochain élément focusable dans le même article
          const nextIndex = (currentIndex + 1) % focusableElements.length;
          focusableElements[nextIndex].focus();
        } else {
          // Passer au prochain article
          const nextArticle = articles[articleIndex + 1];
          if (nextArticle) {
            nextArticle.focus();
          }
        }
        e.preventDefault(); // Empêche le comportement par défaut pour gérer nous-même le focus
      }
    }
  });
});

Pour corriger les erreurs de syntaxe et simplifier le CSS :
.colonne-principale.vignettes {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* Correction de la syntaxe */
  gap: 1.000rem;
}

.longueur-30-lignes .vignette:focus-within::after,
.longueur-40-lignes .vignette:focus-within::after {
  display: none;
}
/* Suppression de la virgule
,
*/

/* Suppression de la règle de hauteur max pour permettre plusieurs paragraphes */
/*
.longueur-30-lignes .vignette:not(:focus-within) > :first-child ~ p,
.longueur-40-lignes .vignette:not(:focus-within) > :first-child ~ p {
  max-height:0;
}
*/

Modifié par Pyanepsion (18 Jul 2024 - 17:11)
Bonsoir,

C'est uniquement ton explication sur le comportement souhaité qui m'a fait réagir ce matin. Mais en fait en analysant un peu plus en détail, pour le bien de l'accessibilité, je renoncerais à l'ensemble de ton truc.

Point n°1:
Devoir donner le focus à l'article et appuyer sur enter pour permettre l'accès aux liens qu'il contient, c'est un comportement non standard.
Qui dit comportement non standard et non intuitivement déductible dit que tu vas devoir l'expliquer clairement d'une manière ou d'une autre à l'utilisateur, et que tu devrais avoir une bonne raison pour imposer ce comportement particulier.
Or ici, je ne vois pas de véritable raison d'imposer ce comportement (le marketing n'est pas une excuse valable à lui seul).

Point n°2:
Avoir un élément parent qui prend le focus, et qui le gère dans les enfants ensuite, c'est quelque chose de très commun, c'est le pattern du composant compsite (composite component).
Mais il y a plusieurs problèmes ici.
1. Ce pattern s'applique bien aux composants d'interface comme des listes, des listes déroulantes, des listes arborescentes, etc. mais pour un article, c'est assez étrange comme façon de voir les choses. On n'a pas intuitivement la notion d'un parent (l'article dans ton cas) qui contient des éléments enfants (les liens de l'article).
2. Le principe de base de ce pattern est que tab ne permet d'atteindre que le sous-élément actuellement actif du composant, la navigation dans les enfants ne se faisant plus par tab ensuite (typiquement, les flèches directionnelles). Un nouvel appui sur tab quitte complètement le composant pour passer au suivant.
Or ici, primo tu n'as pas nécessairement de sous-élément actif, secundo tu continues la navigation avec tab, et tertio tu demandes un appui sur enter pour rendre accessible les enfants. Donc tu ne respectes pas vraiment ce pattern.
ON en revient au premier point, le comportement est inhabituel, pas intuitivement déductible, et donc c'est source de frictions inutiles.
3. Si tu décidais d'implémenter réellement le compposite component pattern, ça n'en reste pas moins étrange dans ce contexte. Si les liens internes à l'article doivent être atteinds avec les flèches directionnelles plutôt que tab, alors tu implémentes quoi qu'il en soit un comportement inhabituel pour des liens d'article.

Point n°3:
On active donc l'accessibilité des liens de l'article en appuyant sur enter lorsque le focus est sur l'article lui-même. Étrange, comme je l'ai déjà dit, mais bon, soit.
Par contre comment on revient à l'état désactivé ensuite ?
Est-ce que c'est automatique ? i.e. sitôt qu'on atteint le deuxième article, le premier se désactive ? Si oui, alors on a un problème de symétrie tab/maj+tab, i.e. appuyer sur tab puis sur maj+tab ne remet pas forcément le focus à sa position initiale; ça peut être très déroutant si on ne comprend pas rapidement la logique.
Est-ce que c'est manuel, i.e. nécéssite une action consciente de l'utilisateur ? Auquel cas il faut une fois de plus expliquer comment faire. Tu pourrais utiliser la touche escape si tu voulais vraiment continuer dans ta logique, ça serait probablement le choix le plus adéquat.
ET si que tu dis que tu t'en fiches si on ne peut pas, ce n'est pas une bonne réponse non plus... Certains utilisateurs peuvent rapidement paniquer s'ils ne peuvent pas revenir en arrière facilement.

Par rapport au CSS, je ne suis pas un expert, mais si je comprends bien, pour les liens externes comme celui qui pointe sur Google, tu masques l'indicateur de focus.
Donc quand le focus est sur ce lien, l'utilisateur ne voit rien, et donc il ne sait pas où il est.
C'est une erreur d'accessibilité fatale !
ON doit toujours pouvoir voir où le focus se trouve. Même si c'est moche, et même si on n'a pas envie d'inciter l'utilisateur à cliquer.

Maintenant plus spécifiquement par rapport au script, je relève notamment que tu te retrouves à réimplémenter toi-même le comportement natif de la touche tab, et de la touche enter sur les liens. C'est une bien mauvaise pratique, et c'est du travail fondamentalement inutile.
Pour tab, ou même de façon générale, tu ne pourras jamais coller exactement au comportement natif, il va forcément y avoir des différences quelque part. C'est vraiment une très mauvaise idée, ça va être un cauchemar à déboguer et à maintenir. Mieux vaut s'abstenir et laisser faire le navigateur qui saura forcément toujours mieux que toi ce qu'il doit faire.
Parfois ça peut être utile, mais il faut avoir une bonne raison pour surcharger un comportement natif.
Pour enter, tu appelles lien.click() et pas e.preventDefault(), du coup ça risque peut-être de provoquer un double clic.

Voilà. Désolé pour ce message assez long. Merci de m'avoir lu jusqu'au bout, et j'espère t'avoir convaincu que tu t'apprêtes à faire une grosse bêtise en matière d'accessibilité au clavier.
Bonjour, QuentinC,
Voilà un point de vue pertinent et fort bien expliqué. Bravo et merci.
Le fond reste que si dans le cas général le comportement standard est attendu, il n’en reste pas moins que sur une page très particulière, une seule page en fait, nous écrivons un article d’actualité et de larges extraits sont publiés sur les réseaux sociaux.
Par souci de transparence, nous indiquons alors le lien vers l’un de ces réseaux, mais la contrepartie est qu’il existe alors un fort risque de perdre le prospect qui ne nous connait pas encore.
Une solution pourrait être de ne pas afficher le lien pour les visiteurs non inscrits tandis que cela renforcera notre présence sociale pour les autres en voyant le lien. Qu’en penses-tu ?
Une autre idée ?
Modifié par Pyanepsion (19 Jul 2024 - 07:46)
a écrit :
Par souci de transparence, nous indiquons alors le lien vers l’un de ces réseaux, mais la contrepartie est qu’il existe alors un fort risque de perdre le prospect qui ne nous connait pas encore.
Une solution pourrait être de ne pas afficher le lien pour les visiteurs non inscrits tandis que cela renforcera notre présence sociale pour les autres en voyant le lien. Qu’en penses-tu ?


Je ne suis pas dans le business des actualités, j'utilise relativement peu les réseaux sociaux, et en particulier je trouve totalement inutile les liens partager sur facebook/instagram/tiktok/etc. aux abords des articles car je ne les utilise jamais.

Donc là par contre je ne comprends absolument pas ton histoire de prospect, encore moins les enjeux qu'il y a derrière, et donc je ne vais donc pas pouvoir beaucoup t'aider.
Modérateur
bonjour,

Ces liens pourrait être géré comme des notes de bas de page/article . Le lien dans l'article serait alors interne avant d'être plus loin, si suivi, un lien externe avec un petit texte.

cdt
Modifié par gcyrillus (19 Jul 2024 - 15:22)
QuentinC a écrit :
j'utilise relativement peu les réseaux sociaux, et en particulier je trouve totalement inutile les liens partager sur facebook/instagram/tiktok/etc. aux abords des articles car je ne les utilise jamais.

Cela dépend évidemment et du type de site et du nombre de suiveurs sur une page sociale.
gcyrillus a écrit :
Ces liens pourrait être géré comme des notes de bas de page/article .

C’est peut-être une idée. Je me dirige pour l’instant plutôt vers une gestion par base de données. Le non-inscrit ne verrait pas ce lien, car nous risquons fort de le perdre s’il quitte le site. L’inscrit pourrait voir ce lien, car cela pourrait lui donner l’idée de nous soutenir sur les réseaux sociaux.