, par opposition au titre de niveau 1 présent entre les balises

– lorsqu’il ne réussit pas accéder au contenu original de la page. Cela souligne l’importance d’avoir des intitulés de liens descriptifs et pertinents. Solution La meilleure approche pour gérer cette problématique SEO consiste à étendre le texte d’ancrage pour inclure des mots-clés pertinents et descriptifs. Par exemple, au lieu d’intituler simplement « Contact » un lien menant à votre formulaire de contact, il serait plus pertinent de l’intituler « Contacter notre agence SEO » ou bien « Découvrir notre audit technique ». Cette approche permet à la fois : d’améliorer la compréhension globale de la structure du site ; de créer des liens bien nommés qui plaisent aux moteurs de recherche. Attention ! Ayez la main légère quand vous placez des mots-clés dans les intitulés de liens : autrement, vous risquez de nuire à l’expérience utilisateur des personnes qui se servent d’un lecteur d’écran, en plus de déclencher une pénalité SEO. Structuration de l’information Titre d’une page web (balise ) Impact pour l’accessibilité Le titre d’une page web doit permettre d’identifier de manière claire, concise et unique les contenus ou la nature de la page. Chaque page doit donc posséder un titre pertinent 11 , c’est-à-dire un titre qui permet aux personnes utilisant des technologies d’assistance, en particulier des lecteurs d’écran, de savoir où elles se trouvent dès le chargement de la page, et de retrouver la page dans leur historique de navigation, ou dans la liste des onglets ouverts dans leur navigateur 12 . Il est très important pour l’accessibilité que le titre de votre page soit rédigé dans la même langue que les contenus de la page eux-mêmes 13  : cela permet aux lecteurs d’écran de lire l’information dans la langue appropriée. Impact pour le SEO Pour le référencement, la balise <title> n’est plus ce qu’elle était, puisque Google la réécrit dans de nombreux cas, sans vous demander votre avis. Ce « méta-titre » est un élément SEO si stratégique qu’il a mené à toutes sortes d’abus et de contenus trompeurs 14 . C’est pourquoi, à partir de 2012, Google a commencé à réécrire certains titres lui-même, en particulier quand il les jugeait trop longs. Capture d’écran 1 : Exemple de réécriture de méta-titre par Google. Sur un site web, le méta-titre est « Prévision des couleurs : d’où viennent les tendances de couleurs ? ». Capture d’écran 2 : Google a raccourci le titre visible sur la capture d’écran précédente en remplaçant la deuxième occurrence du mot « couleurs » par des points de suspension. Aujourd’hui, Google va encore plus loin : la balise <title> est désormais un élément dynamique qu’il peut entièrement réécrire s’il estime que votre titre initial n’est pas intéressant pour les utilisateurs et utilisatrices, ou s’il présente l’un des problèmes ci-dessous : titre sémantiquement pauvre : votre titre reflète mal le contenu de la page, et ne communique pas assez de valeur pour les personnes qui consulteront les résultats de Google ; incohérences linguistiques : un titre en anglais pour une page en français perturbe la compréhension de votre page par Google, en plus de beaucoup nuire à l’expérience utilisateur des personnes utilisant un lecteur d’écran ; titres inexacts : Google considère comme du clickbait (« piège à clics ») les titres qui ne reflètent pas le contenu réel de la page ; titres obsolètes : Google valorise l’exactitude temporelle. Par exemple, si vous avez publié un article intitulé « Quelles sont les couleurs tendance en 2023 ? », ce serait une mauvaise pratique de le modifier l’année suivante en écrivant « Quelles sont les couleurs tendance en 2024 ? », dans l’espoir de tromper Google sur la fraîcheur de votre article ; Capture d’écran 3 : un résultat dans Google indique que le site westwing.fr contient une page consacrée aux couleurs tendance, intitulée « Déco : quelles sont les couleurs tendance en 2023 ? » titres répétitifs : la répétition du même titre sur différentes pages est un obstacle pour le référencement. Google a besoin d’identifier et de différencier le caractère unique de chaque page web afin de présenter des résultats pertinents dans son moteur de recherche ; titre trop court : Google semble favoriser l’utilisation optimale de l’espace visuel au sein des résultats de recherche. Un titre trop court peut indiquer un titre insuffisamment pertinent. Si Google estime que votre « méta-titre » présente l’un de ces problèmes, il va le réécrire en utilisant plusieurs sources issues de votre page web : balise <title> ; titre de niveau 1 (balise h1) ; balise meta Open Graph og:title ; contenus proéminents ; ancres de texte sur les liens qui pointent vers votre page depuis d’autres sites web ; données structurées. Solution Pour écrire un « méta-titre » à la fois pertinent pour l’accessibilité et que Google ne va pas réécrire (enfin, on l’espère…) : choisissez un titre unique, descriptif et concis pour chaque page, sans céder à la tentation du « keyword stuffing » 15  ; rédigez le titre dans la même langue que le contenu de la page ; si la page est paginée 16  : incluez le numéro de la page dans son titre. Néanmoins, gardez en tête que Google dépriorise les pages paginées dans son index, car il les considère comme du contenu de moindre intérêt (« thin »). Enfin, pour améliorer l’UX et le SEO, la marque ou le titre de votre site devraient être ajoutés dans le titre de chaque page, généralement après un séparateur. Titres dans le contenu de la page (balises h1, h2, h3, etc.) Impact pour l’accessibilité Le contenu et la hiérarchie des titres de niveau 1 à 6 doivent être pertinents pour permettre aux personnes utilisant des technologies d’assistance de comprendre la structure de vos pages, et d’y naviguer à l’aide des raccourcis clavier fournis par les lecteurs d’écran 17 . Avoir une hiérarchie stricte entre les différents niveaux de titres est une bonne pratique pour l’accessibilité. Cependant, il n’est pas interdit, dans une même page web : de sauter un niveau de titre (par exemple, un h4 qui suit un h2) ; d’avoir plusieurs h1 ; de n’avoir aucun h1 ; de n’avoir aucun titre du tout. Pour mieux connaître ces subtilités, lisez l’article de David Swallow : En-têtes et non-conformité aux WCAG : une clarification à juste titre. Impact pour le SEO Google accorde une attention particulière à la structure sémantique du contenu : les balises de titre sont donc stratégiques pour l’aider à comprendre l’organisation et la pertinence de votre contenu, et ainsi maximiser son impact SEO. Vous devez donc soigner la rédaction des titres de vos contenus web. Les titres doivent être naturels et descriptifs, en intégrant les mots-clés pertinents pour votre stratégie, tout en évitant le piège du keyword stuffing, déjà évoqué. Voici la question principale que se posent les expert·es SEO : « Est-il possible de comprendre de quel sujet traite la page en lisant uniquement la hiérarchie des titres qui y sont présents ? ». Dans l’exemple suivant, il sera compliqué pour Google de comprendre qu’il s’agit de la page d’accueil de la station de ski Mont-Tremblant située au Québec, en consultant uniquement les titres de la page. Hiérarchie de titres HTML imparfaite. Le titre principal est « Réserver maintenant » en h1, suivi de sous-sections comme « Hébergement », « Ski », « Leçons », « Activités » et « Location » en h2. Les titres suivants, intitulés « Vacances d’hiver », « Billets de ski 2024/25 » et « Escapade d’automne », sont structurés avec une balise h6 : cela perturbe la structure, car il manque les niveaux intermédiaires h3, h4, et h5. Enfin, un dernier titre h3 est utilisé pour la section « Ouverture de la saison de ski ». La hiérarchie des titres est aussi importante du point de vue SEO, car Google ne prête pas la même attention aux titres au-delà du niveau 3. Pour plus d’informations, consultez les recommandations officielles de Google pour rédiger des titres (en anglais). Enfin, pour optimiser la hiérarchie de contenu d’une page, demandez-vous pour chaque titre : « Ce titre permet-il de mieux comprendre le thème du contenu principal de la page ? ». Solution Privilégiez les balises HTML natives pour titrer vos contenus : h1, h2, h3, h4, h5, h6. Si votre expert·e SEO vous déconseille d’utiliser une balise native dans des cas très précis : utilisez, ponctuellement, un titre WAI-ARIA avec role="heading" (en anglais) et la propriété aria-level adaptée. Prenons un exemple. Si le pied de page de votre site contient en permanence un bloc permettant de s’abonner à votre newsletter, il ne serait pas pertinent, côté SEO, d’utiliser une balise HTML h2 ou h3 pour titrer ce bloc. En effet, comme ce titre est commun à plusieurs pages, il ne renforcera pas le caractère unique et original de chaque page par rapport aux autres pour Google. Toutefois, pour l’accessibilité, il est essentiel que ce bloc dispose d’un titre : c’est ce qui va permettre aux personnes handicapées utilisant une technologie d’assistance de se repérer dans la page et d’y naviguer plus facilement. C’est pourquoi, dans ce cas précis, avoir recours à un titre WAI-ARIA offre un compromis intéressant pour prioriser l’accessibilité tout en défendant vos intérêts SEO. Ainsi, sur le site d’Access42, nous avons décidé de titrer le bloc « Recevez votre veille sur l’accessibilité numérique par e-mail » avec un titre ARIA de niveau 2. Il n’y a pas de balise HTML h2 : en effet, écrire <p role="heading" aria-level="2">Recevez votre veille sur l’accessibilité numérique par e-mail</p> équivaut, pour les lecteurs d’écran, à écrire <h2>Recevez votre veille sur l’accessibilité numérique par e-mail</h2>, mais sans l’éventuel impact négatif dans Google lié à la redondance du bloc d’une page à l’autre. Attention ! ARIA peut nuire à l’accessibilité si vous l’utilisez mal. Voici des erreurs courantes à éviter absolument : de manière générale, n’utilisez pas les propriétés role="heading" et aria-level sur les balises h1, h2, h3, h4, h5, h6, car il est important de respecter la sémantique des éléments HTML ; n’utilisez pas la propriété role="presentation" sur les balises h1, h2, h3, h4, h5, h6 pour la même raison ; n’utilisez pas aria-hidden="true" sur des textes masqués visuellement avec une classe CSS comme .sr-only, dans l’espoir d’éviter leur indexation par Google. Tout ce que vous allez faire, c’est rendre ces titres inaccessibles pour les personnes handicapées, sans empêcher leur référencement ; n’utilisez pas aria-hidden="true" sur les titres h1, h2, h3, h4, h5, h6 pour empêcher qu’ils ne soient pris en compte par Google : rebelote, celui-ci n’en tiendra pas compte, mais vous empêcherez les personnes handicapées de naviguer dans votre page. Cas où l’accessibilité et le SEO s’opposent 💣 – et où l’accessibilité doit primer ! Citations : ne pas confondre la balise <cite> (accessibilité) et l’attribut cite (SEO) Impact pour l’accessibilité Vous l’avez compris : une des clés de l’accessibilité web, c’est le respect de la sémantique HTML. Tout comme les paragraphes, les listes ou les titres, les citations nécessitent elles aussi d’être correctement structurées 18 . Une distinction doit être faite entre les citations courtes (balise q), et les blocs de citation (balise blockquote). La balise cite (en anglais), quant à elle, peut être utilisée pour citer le titre d’une œuvre dont est tirée une citation, comme dans l’exemple suivant : <p>La citation suivante est un extrait du livre <cite>Survivre au taf. Stratégies d’autodéfense pour personnes minorisées</cite> de Marie Dasylva :</p> <blockquote> <p>Nous avons donc travaillé sur la notion d’excellence, et je le répète : l’excellence, la vraie, est celle qui ne vous tue pas. (…) Déconstruire cette notion commence par le fait d’accepter de devoir s’organiser autour de son handicap au lieu de vouloir montrer qu’on l’a dépassé à tout prix.</p> </blockquote> Ne confondez pas la balise <cite> avec l’attribut cite, qui contient une URI ou une URL menant à la source de la citation. Cet attribut est utilisé uniquement à des fins de SEO, étant donné qu’il est inaccessible aux êtres humains dans la plupart des cas, sauf lorsqu’on utilise le lecteur d’écran JAWS a priori 19 . Impact pour le SEO Les directives de qualité E-E-A-T (Expérience, Expertise, Autorité, Fiabilité) et la mise à jour HCU 20 de Google soulignent l’importance des citations dans le référencement web, bien qu’elles ne soient pas des facteurs de classement directs. Les citations renforcent la crédibilité de votre contenu en l’enrichissant et en soulignant son niveau d’expertise. C’est un outil stratégique qui démontre la fiabilité de votre site et qui, par extension, soutient votre autorité de marque. Cependant, si Google n’accorde aucun traitement spécial au contenu placé dans une balise blockquote, une citation trop longue peut être perçue comme du contenu dupliqué. Cela veut dire que le moteur de recherche peut choisir de ne pas indexer certaines pages considérées comme dupliquées à cause d’un contenu trop similaire à celui d’une autre page. Par ailleurs, l’attribut cite a plusieurs fonctions techniques, mais son impact SEO est limité. S’il peut être lu par les moteurs de recherche, en revanche il ne protège pas contre le contenu dupliqué. Solution Distinguez les citations courtes des blocs de citations grâce aux balises HTML appropriées : q et blockquote. Si besoin, citez l’œuvre dont est extraite chaque citation à l’aide de la balise <cite>. Pour optimiser l’impact SEO d’une citation, utilisez l’attribut cite sur la balise blockquote pour indiquer l’URL dont elle est issue. Privilégiez les sources académiques et les études originales, et veillez à limiter le contenu cité par rapport à votre propre contenu, pour éviter qu’il ne soit considéré comme du contenu dupliqué par Google. Pour en savoir plus, consultez des exemples dans l’article de Rodolphe sur le blog d’Alsacréations : Les citations en HTML avec blockquote, cite et q. Cas qui peuvent créer des obstacles pour l’accessibilité et le SEO 🙈 Liens Liens composites Un lien composite est un bloc entièrement cliquable (balise HTML a) qui regroupe à la fois du texte ainsi qu’un ou plusieurs éléments de type image (balises img, area, object, canvas ou svg). Impact pour l’accessibilité Un lien composite ne pose pas, en soi, de problème pour l’accessibilité, si son intitulé est pertinent, c’est-à-dire s’il permet de comprendre la fonction et la destination du lien 21 . C’est particulièrement important pour les personnes qui présentent une déficience visuelle et qui n’ont pas une vision globale de la page : un intitulé de lien pertinent leur permet de comprendre la fonction de chaque lien, qu’il contienne une image, du texte, ou les deux à la fois. Créer des liens composites est même une bonne pratique pour supprimer la redondance créée par deux liens adjacents menant à la même URL (par exemple un lien image puis un lien texte). En effet, selon les personnes, il peut être plus ou moins facile d’identifier un lien grâce à une icône, ou bien grâce à son intitulé : réunir les deux au sein d’un seul et unique lien permet donc d’améliorer son accessibilité 22 . Maintenant, un lien composite qui contiendrait beaucoup de texte peut nuire à l’expérience utilisateur, notamment pour les personnes utilisant un lecteur d’écran, la mémoire tampon de ces outils étant limitée, la restitution de l’intitulé pourrait être hachée, ce qui peut rendre complexe sa compréhension. Impact pour le SEO Au niveau SEO, les blocs entièrement cliquables empêchent les robots de Google de comprendre l’ancre explicitement sans le contexte des autres éléments du bloc. Il vaut mieux conserver des liens distincts, et étendre la zone de clic si besoin avec CSS et/ou JavaScript. En effet, la longueur du texte d’ancrage n’est pas un critère déterminant pour le SEO, car Google privilégie les intitulés de lien pertinents et naturels qui décrivent la destination du lien. Dans l’article Internal Linking Anchor Texts - Text Variety is Key According to SEO Studies, Daniel Højris Bæk démontre que les sites privilégiant la diversité des textes d’ancrage surpassent leurs concurrents avec un classement SEO moyen de 1,3 (contre 3,5) et un engagement utilisateur supérieur de 50 %, la longueur optimale d’un intitulé de lien se situant autour de 5 mots ou 24 caractères. Pour en savoir plus : Anchor Text : Short vs. Long – What Is Best for SEO? Anchor Text : Types, SEO Implications, and Best Practices The Importance of Anchor Text Diversity Solution Faites attention à la longueur de l’intitulé des liens composites. Si besoin, étendez la zone de clic du lien tout en vous assurant que chaque lien (balise a) reste accessible au clavier. Dans l’article Enhancing The Clickable Area Size, Robin Rendle partage des ressources proposant plusieurs manières de faire. Obfuscation des liens L’obfuscation des liens est une pratique très controversée (pour ne pas dire : d’un autre âge) qui consiste à masquer ou modifier la présentation des liens pour influencer les moteurs de recherche. Cela pose de sérieux problèmes, à la fois pour l’accessibilité et le SEO. Impact pour l’accessibilité Un lien obfusqué pose un problème majeur pour les personnes qui naviguent au clavier, dont des personnes ayant un handicap moteur et/ou un handicap visuel. Voici un exemple de lien obfusqué : <span data-lien="/mentions-legales">Mentions légales</span>. Visuellement, cela peut ressembler à un lien véritable. Techniquement, un script va détecter la cible de ce faux lien, et ouvrir l’URL correspondante lorsque l’utilisatrice cliquera dessus avec sa souris ou son trackpad. Le problème, c’est que le focus ne peut pas être déplacé sur ce faux lien. Non contents de ruiner l’accessibilité de votre page, les faux liens posent d’autres problèmes d’utilisabilité, qui impactent un très large public, comme l’explique très bien Julie Moynat dans l’article Obfuscation de liens en SEO et problèmes d’accessibilité. Impact pour le SEO Google a pris fermement position contre l’obfuscation des liens, qu’il considère comme du « black hat SEO », c’est-à-dire comme une mauvaise pratique qui va à l’encontre de ses recommandations. Selon lui, obfusquer un lien est une tentative de manipulation du référencement. Si vous utilisez ce type de méthode, votre site risque donc d’être lourdement pénalisé dans les résultats de recherche de Google. L’obfuscation des liens est souvent utilisée pour masquer les liens qui mènent à des contenus affiliés. Or, selon John Mueller, représentant de Google (lien en anglais), les liens d’affiliation sont parfaitement acceptables : il est inutile de les masquer ou de les obfusquer sous prétexte de vouloir conserver un bon référencement. Solution N’obfusquez pas les liens, même pas les liens affiliés : n’utilisez pas JavaScript pour modifier la cible des liens sur les actions « onclick » ni d’autres méthodes de type « cloaking » (c’est-à-dire servir un contenu à Google tout en en servant un autre aux êtres humains). Utilisez l’attribut rel="sponsored" sur chaque lien affilié. Cela permet de signaler clairement la nature commerciale du lien aux moteurs de recherche. Cette transparence vous évitera des impacts négatifs sur votre référencement. Si vous n’avez vraiment pas le choix, optez pour une méthode d’obfuscation accessible : elle doit notamment permettre d’atteindre chaque faux lien au clavier, à la voix, ou au moyen d’un lecteur d’écran. Pour en savoir plus, consultez l’idée pour obfusquer les liens de façon accessible proposée par Julie Moynat et Romain Gervois. Attention ! L’obfuscation de liens, même avec une approche plus accessible, est une pratique non seulement dépassée, mais surtout risquée du point de vue du SEO : en effet, Google peut l’interpréter comme une tentative de manipulation qui va à l’encontre de ses directives (en anglais). De plus, si vous utilisez JavaScript pour transformer les faux liens en vrais liens, cela peut ralentir le chargement de la page. Or, Google tient aussi compte de la vitesse de chargement dans son algorithme (en anglais). CQFD. Question subsidiaire : Google tient-il compte de l’accessibilité pour le référencement ? Sundar Pichai, président-directeur général de Google depuis 2015, affirme que l’accessibilité est une valeur fondamentale de l’entreprise, inscrite dans leur mission et appliquée à leurs propres produits (en anglais). Google cherche à prouver cet engagement en partageant, pour l’ensemble de leurs services, les rapports sur leur conformité à l’accessibilité. Cependant, John Mueller, spécialiste des questions de référencement et porte-parole de Google, a adopté une position plus nuancée concernant les sites web tiers dans une interview de 2022 (en anglais) : selon lui, l’accessibilité n’est pas un facteur direct de classement et Google n’a pas pour projet d’en faire un éventuel critère de pertinence. La raison principale tient, selon lui, à l’impossibilité technique de quantifier objectivement l’accessibilité d’un site web. Il n’exclut pas que cela puisse changer à l’avenir en évoquant un système de mesure de l’accessibilité similaire aux Core Web Vitals de Google, un outil permettant de mesurer la qualité d’une page web. Cette possibilité serait d’autant plus pertinente avec l’entrée en vigueur de nouvelles réglementations à travers le monde. Toutefois, à ce jour, Google ne semble pas avoir lancé de véritable projet en ce sens. Capture d’écran 4 : Google does not give a damn about accessibility. But we as humans should. Extrait de la conférence How to ensure that your technical SEO strategy is truly accessible que Billie Geena a donnée à Brighton SEO en 2023. Conclusion L’accessibilité et le SEO ne sont pas fondamentalement incompatibles, mais leur relation reste souvent déséquilibrée. Si de nombreuses bonnes pratiques peuvent servir les deux objectifs, il est essentiel de garder à l’esprit que l’accessibilité doit toujours primer sur le SEO. Cette hiérarchisation est cruciale. En effet, le SEO repose sur des algorithmes dont le principal objectif est la génération de profit, tandis que l’accessibilité vise à répondre aux besoins et droits fondamentaux des personnes en situation de handicap, en leur permettant d’accéder au web et aux outils numériques sans obstacle. C’est pourquoi, affirmer que l’accessibilité améliorerait le SEO est risqué : lorsque les deux entrent en conflit, c’est souvent le SEO qui l’emporte, au détriment des besoins humains. La solution réside dans une approche « accessibility first », similaire au concept « mobile first » (le mobile d’abord). En concevant d’abord pour l’accessibilité, vous créez des interfaces qui répondent aux besoins des utilisateurs et des utilisatrices, tout en anticipant l’évolution des moteurs de recherche vers plus d’inclusivité. Car si Google affirme aujourd’hui que l’accessibilité n’est pas un facteur de classement direct, les nouvelles réglementations et l’évolution des usages le forceront tôt ou tard à intégrer ces principes dans ses outils pour rester pertinent. C’est donc à vous, professionnel·les du numérique, de montrer l’exemple en plaçant l’accessibilité au cœur de tous vos projets numériques. Cela implique de prendre en compte les besoins des personnes handicapées dès la phase de conception, puis d’adapter votre stratégie de référencement en conséquence. La réussite d’une démarche « accessibility first » dépend aussi beaucoup des bons choix de prestataires. Que ce soit pour le design UX/UI, le développement, la production éditoriale, la gestion de projet, l’accessibilité ou le SEO, il est essentiel de travailler avec des expert·es qui comprennent les enjeux de l’accessibilité. En particulier, les expert·es SEO ayant cette sensibilité sauront ajuster leurs recommandations pour allier référencement et accessibilité, contribuant ainsi à la réussite de vos projets web. Remerciements Nous adressons nos plus sincères remerciements aux personnes ayant permis d’enrichir cet article et de vérifier son accessibilité : Audrey Maniez, Luce Carević, Maïa Kopff et Philippe Bouchon d’Access42, ainsi que l’équipe de Paris Web. Search Engine Optimization ↩ Retour au texte 1 ARIA permet de rendre accessibles des composants complexes, en ajoutant de la sémantique et des métadonnées aux contenus HTML (Hypertext Markup Language) pour les lecteurs d’écran. ↩ Retour au texte 2 La norme européenne est également utilisée ailleurs dans le monde, par exemple au Canada, qui en fournit une version HTML. ↩ Retour au texte 3 cf. critère 1.2 du RGAA ↩ Retour au texte 4 Malheureusement, l’option n’est pas disponible dans les éditeurs WYSIWYG de manière native. Si besoin, rapprochez-vous de l’équipe qui développe votre site ou votre thème pour leur faire part de cette demande. ↩ Retour au texte 5 cf. test 1.2.1 du RGAA ↩ Retour au texte 6 cf. cette note en anglais ↩ Retour au texte 7 cf. cette vidéo en anglais : Je me concentrerais davantage sur l’aspect de l’accessibilité que sur l’aspect purement SEO. selon John Mueller, Search Liaison chez Google. Cependant, les bonnes pratiques officielles de Google ne traitent pas des images décoratives : cela peut laisser penser qu’il serait essentiel de renseigner l’alternative de toutes les images, alors que c’est une mauvaise pratique pour l’accessibilité. ↩ Retour au texte 8 cf. critère 6.1 et critère 6.2 du RGAA ↩ Retour au texte 9 Il s’agit du critère de succès 2.4.9 Fonction du lien (lien uniquement) dans les WCAG 2.1, qui est de niveau triple A (AAA). Bien que ce critère faisait l’objet du critère 6.3 dans le RGAA 3, il a été supprimé du RGAA 4. Il est simplement cité dans une de ses annexes. ↩ Retour au texte 10 cf. critère 8.5 et critère 8.6 du RGAA ↩ Retour au texte 11 cf. test 8.6.1 du RGAA ↩ Retour au texte 12 cf. critères 8.3 et critère 8.4 du RGAA ↩ Retour au texte 13 Par exemple : insérer de fausses dates dans le titre pour mieux ressortir dans les résultats de Google ; ou encore décrire des contenus de manière trompeuse dans le titre d’une page, par exemple en incluant une marque populaire de chaussures dans le titre d’une page pour faire référencer une page vendant des chaussures, même si celles-ci ne sont pas de la marque indiquée. ↩ Retour au texte 14 Le « keyword stuffing » consiste à ajouter des mots clés de manière excessive et artificielle dans l’espoir d’améliorer le référencement d’une page. ↩ Retour au texte 15 Par exemple, les nombreuses pages d’un même résultat de recherche. Un ensemble de pages paginées s’appelle une « collection de pages » dans le vocabulaire du RGAA. Cette notion se trouve encore dans le RGAA 4. ↩ Retour au texte 16 cf. critère 9.1 du RGAA ↩ Retour au texte 17 cf. critère 9.4 du RGAA ↩ Retour au texte 18 Seul JAWS restitue la présence de cet attribut dans certains contextes. Pour en savoir plus : Blockquotes in Screen Readers d’Adrian Roselli et HTML5 Accessibility Chops: section elements de Steve Faulkner (cf. le tableau sur JAWS). ↩ Retour au texte 19 E-E-A-T évalue la qualité du contenu, tandis que le HCU favorise le contenu utile pour les utilisateurs et utilisatrices. ↩ Retour au texte 20 cf. test 6.1.3 du RGAA ↩ Retour au texte 21 cf. Technique WCAG H2: Combining adjacent image and text links for the same resource ↩ Retour au texte 22"> <meta property="og:image" content="https://feedbot.net/storage/thumbnails/2024_12/ac5b1d08f9771e2f557a8412fde05333-639a385ec0a0e0b441c3c8268c905658.jpg"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:site" content="@feedbotnet"> <meta name="twitter:title" content="Accessibilité versus SEO : le duel qui façonne le web"> <meta property="twitter:description" content="Google est souvent qualifié de « premier utilisateur aveugle du Web ». Cette idée mérite d’être déconstruite, car contrairement aux personnes en situation de handicap qui cherchent simplement à consulter et à publier des contenus numériques, les robots de Google œuvrent uniquement pour la performance commerciale. Comme chien et chat ? Photo de Alexander Grey pour Pexels. Malgré tout, concilier accessibilité et référencement (SEO 1 ) est possible à condition de prioriser les besoins des êtres humains par rapport aux algorithmes des moteurs de recherche, qu’ils soient géants comme Google et Bing, ou alternatifs comme Ecosia et DuckDuckGo. Ce constat nous pousse à mêler nos expertises respectives pour déconstruire quelques idées reçues à ce sujet, et trouver un équilibre entre besoins humains et exigences algorithmiques. Étant donné la domination de Google en matière de SEO, nous avons centré cet article sur son moteur de recherche. Avant de commencer, vous devez savoir que l’accessibilité du web est définie et standardisée par plusieurs normes de référence : au niveau mondial : les Règles pour l’accessibilité des contenus web (WCAG, Web Content Accessibility Guidelines) et la spécification WAI-ARIA (en anglais) 2 du W3C (World Wide Web Consortium) ; dans l’Union européenne : la norme EN 301 549 (PDF en anglais) 3  ; en France : le Référentiel général d’amélioration de l’accessibilité (RGAA). Dans cet article, nous nous référons à la version 4.1.2 du RGAA, qui est la version en vigueur. Une démarche « accessibility first » (l’accessibilité d’abord) permettant d’optimiser votre référencement naturel, ça vous dit ? Alors, lisez ce qui suit. Si vous ne lisez pas tout, lisez au moins la conclusion, qui présente des considérations éthiques très importantes. Cas où l’on peut concilier l’accessibilité et le SEO 🤝 Images : référencer une image décorative Impact pour l’accessibilité Une image est décorative lorsqu’elle n’a aucune fonction et ne véhicule aucune information particulière par rapport au contenu auquel elle est associée, selon la définition du RGAA. Les images décoratives doivent être ignorées par les technologies d’assistance telles que les lecteurs d’écran dont se servent en particulier les personnes déficientes visuelles 4 . En effet, un lecteur d’écran leur permet d’accéder aux contenus et aux fonctionnalités d’un site ou d’une application web en restituant ou vocalisant l’information. Or, si toutes les images de décoration du web étaient restituées, cela produirait beaucoup d’informations inutiles et alourdirait la charge cognitive des personnes concernées. Le problème, c’est que des expert·es SEO souhaitent parfois optimiser toutes les images pour le référencement en remplissant leurs alternatives avec des mots-clés, même celles des images décoratives. Alors, que faire ? Solution Voici comment indexer une image décorative dans Google sans générer de bruit inutile pour les personnes aveugles ou malvoyantes : ajoutez l’attribut ARIA aria-hidden="true" sur chaque image décorative que vous tenez absolument à référencer 5 , puis renseignez l’alternative textuelle de l’image à l’aide des mots-clés de votre choix. C’est une méthode parfaitement conforme au RGAA 6  ; a contrario, ne remplissez pas l’alternative textuelle d’une image décorative si vous ne souhaitez pas qu’elle apparaisse dans les résultats de recherche d’images. Attention ! N’utilisez pas l’attribut aria-hidden="true" sur un composant interactif, tel qu’un lien, un bouton, un carrousel, etc. En effet, cela empêcherait les personnes qui naviguent à la voix d’interagir avec ce composant 7 , ce qui serait un grave problème d’accessibilité. N’utilisez pas le role="presentation" pour masquer les images décoratives à référencer. Certes, le test 1.2.1 du RGAA reconnaît comme décorative une image possédant à la fois une alternative textuelle renseignée et une propriété WAI-ARIA role="presentation". Mais les tests effectués par Access42 indiquent que, dans Firefox, tous les lecteurs d’écran préconisés par l’environnement de test du RGAA restituent l’image quand même. ☹ N’oubliez pas que les mots-clés utilisés dans l’alternative textuelle des images décoratives possédant la propriété aria-hidden="true" s’afficheront à l’écran si l’image ne se charge pas pour une raison ou pour une autre. Impact pour le SEO Google ne tient pas compte des attributs WAI-ARIA pour l’indexation et le référencement des pages web. Cette information a été confirmée par plusieurs experts SEO dans une discussion sur LinkedIn (en anglais), dont John Mueller et Martin Splitt de l’équipe Google Search Relations. De plus, l’attribut alt n’est pas un levier SEO magique : il doit avant tout servir l’accessibilité plutôt que d’être détourné pour le référencement, dixit Google lui-même 8 . Cela veut dire que bourrer l’attribut alt de mots-clés, même avec aria-hidden="true", est non seulement inutile, mais aussi potentiellement négatif pour votre référencement. Liens : intitulés des liens Parlons maintenant des liens aux intitulés a priori peu pertinents, comme « cliquer ici » ou « en savoir plus ». Impact pour l’accessibilité Ce type de liens ne pose pas de problème particulier pour l’accessibilité de votre site, tant que le contexte de chaque lien permet d’en comprendre la fonction et la destination. Chaque lien doit donc être explicite et posséder un intitulé 9 . Cela vous laisse une grande marge de manœuvre pour intituler vos liens comme bon vous semble. Certes, du point de vue de l’expérience utilisateur, une meilleure pratique consisterait à rendre chaque intitulé de lien explicite indépendamment de son contexte, comme le recommandent les WCAG 10 . Bien que cela soit facultatif, cette pratique contribue à améliorer l’expérience des personnes qui utilisent un lecteur d’écran, car elle leur permet de consulter la liste de tous les liens présents dans une page web et de comprendre leur fonction et leur destination, sans avoir à consulter le contexte de chacun d’eux. Impact pour le SEO Mais revenons à vos liens « cliquer ici » ou « en savoir plus » : ils sont en général déconseillés pour le SEO, car ils n’apportent aucune valeur sémantique aux moteurs de recherche et ne transmettent pas la valeur SEO d’une page à l’autre de manière efficace. Ce type de liens représente une opportunité manquée (en anglais) d’optimiser le maillage interne de votre site (en anglais). Pire : Google utilise parfois le texte d’ancrage des liens externes pour générer le « méta-titre » d’une page web – c’est-à-dire le titre présent entre les balises <title>, par opposition au titre de niveau 1 présent entre les balises

– lorsqu’il ne réussit pas accéder au contenu original de la page. Cela souligne l’importance d’avoir des intitulés de liens descriptifs et pertinents. Solution La meilleure approche pour gérer cette problématique SEO consiste à étendre le texte d’ancrage pour inclure des mots-clés pertinents et descriptifs. Par exemple, au lieu d’intituler simplement « Contact » un lien menant à votre formulaire de contact, il serait plus pertinent de l’intituler « Contacter notre agence SEO » ou bien « Découvrir notre audit technique ». Cette approche permet à la fois : d’améliorer la compréhension globale de la structure du site ; de créer des liens bien nommés qui plaisent aux moteurs de recherche. Attention ! Ayez la main légère quand vous placez des mots-clés dans les intitulés de liens : autrement, vous risquez de nuire à l’expérience utilisateur des personnes qui se servent d’un lecteur d’écran, en plus de déclencher une pénalité SEO. Structuration de l’information Titre d’une page web (balise ) Impact pour l’accessibilité Le titre d’une page web doit permettre d’identifier de manière claire, concise et unique les contenus ou la nature de la page. Chaque page doit donc posséder un titre pertinent 11 , c’est-à-dire un titre qui permet aux personnes utilisant des technologies d’assistance, en particulier des lecteurs d’écran, de savoir où elles se trouvent dès le chargement de la page, et de retrouver la page dans leur historique de navigation, ou dans la liste des onglets ouverts dans leur navigateur 12 . Il est très important pour l’accessibilité que le titre de votre page soit rédigé dans la même langue que les contenus de la page eux-mêmes 13  : cela permet aux lecteurs d’écran de lire l’information dans la langue appropriée. Impact pour le SEO Pour le référencement, la balise <title> n’est plus ce qu’elle était, puisque Google la réécrit dans de nombreux cas, sans vous demander votre avis. Ce « méta-titre » est un élément SEO si stratégique qu’il a mené à toutes sortes d’abus et de contenus trompeurs 14 . C’est pourquoi, à partir de 2012, Google a commencé à réécrire certains titres lui-même, en particulier quand il les jugeait trop longs. Capture d’écran 1 : Exemple de réécriture de méta-titre par Google. Sur un site web, le méta-titre est « Prévision des couleurs : d’où viennent les tendances de couleurs ? ». Capture d’écran 2 : Google a raccourci le titre visible sur la capture d’écran précédente en remplaçant la deuxième occurrence du mot « couleurs » par des points de suspension. Aujourd’hui, Google va encore plus loin : la balise <title> est désormais un élément dynamique qu’il peut entièrement réécrire s’il estime que votre titre initial n’est pas intéressant pour les utilisateurs et utilisatrices, ou s’il présente l’un des problèmes ci-dessous : titre sémantiquement pauvre : votre titre reflète mal le contenu de la page, et ne communique pas assez de valeur pour les personnes qui consulteront les résultats de Google ; incohérences linguistiques : un titre en anglais pour une page en français perturbe la compréhension de votre page par Google, en plus de beaucoup nuire à l’expérience utilisateur des personnes utilisant un lecteur d’écran ; titres inexacts : Google considère comme du clickbait (« piège à clics ») les titres qui ne reflètent pas le contenu réel de la page ; titres obsolètes : Google valorise l’exactitude temporelle. Par exemple, si vous avez publié un article intitulé « Quelles sont les couleurs tendance en 2023 ? », ce serait une mauvaise pratique de le modifier l’année suivante en écrivant « Quelles sont les couleurs tendance en 2024 ? », dans l’espoir de tromper Google sur la fraîcheur de votre article ; Capture d’écran 3 : un résultat dans Google indique que le site westwing.fr contient une page consacrée aux couleurs tendance, intitulée « Déco : quelles sont les couleurs tendance en 2023 ? » titres répétitifs : la répétition du même titre sur différentes pages est un obstacle pour le référencement. Google a besoin d’identifier et de différencier le caractère unique de chaque page web afin de présenter des résultats pertinents dans son moteur de recherche ; titre trop court : Google semble favoriser l’utilisation optimale de l’espace visuel au sein des résultats de recherche. Un titre trop court peut indiquer un titre insuffisamment pertinent. Si Google estime que votre « méta-titre » présente l’un de ces problèmes, il va le réécrire en utilisant plusieurs sources issues de votre page web : balise <title> ; titre de niveau 1 (balise h1) ; balise meta Open Graph og:title ; contenus proéminents ; ancres de texte sur les liens qui pointent vers votre page depuis d’autres sites web ; données structurées. Solution Pour écrire un « méta-titre » à la fois pertinent pour l’accessibilité et que Google ne va pas réécrire (enfin, on l’espère…) : choisissez un titre unique, descriptif et concis pour chaque page, sans céder à la tentation du « keyword stuffing » 15  ; rédigez le titre dans la même langue que le contenu de la page ; si la page est paginée 16  : incluez le numéro de la page dans son titre. Néanmoins, gardez en tête que Google dépriorise les pages paginées dans son index, car il les considère comme du contenu de moindre intérêt (« thin »). Enfin, pour améliorer l’UX et le SEO, la marque ou le titre de votre site devraient être ajoutés dans le titre de chaque page, généralement après un séparateur. Titres dans le contenu de la page (balises h1, h2, h3, etc.) Impact pour l’accessibilité Le contenu et la hiérarchie des titres de niveau 1 à 6 doivent être pertinents pour permettre aux personnes utilisant des technologies d’assistance de comprendre la structure de vos pages, et d’y naviguer à l’aide des raccourcis clavier fournis par les lecteurs d’écran 17 . Avoir une hiérarchie stricte entre les différents niveaux de titres est une bonne pratique pour l’accessibilité. Cependant, il n’est pas interdit, dans une même page web : de sauter un niveau de titre (par exemple, un h4 qui suit un h2) ; d’avoir plusieurs h1 ; de n’avoir aucun h1 ; de n’avoir aucun titre du tout. Pour mieux connaître ces subtilités, lisez l’article de David Swallow : En-têtes et non-conformité aux WCAG : une clarification à juste titre. Impact pour le SEO Google accorde une attention particulière à la structure sémantique du contenu : les balises de titre sont donc stratégiques pour l’aider à comprendre l’organisation et la pertinence de votre contenu, et ainsi maximiser son impact SEO. Vous devez donc soigner la rédaction des titres de vos contenus web. Les titres doivent être naturels et descriptifs, en intégrant les mots-clés pertinents pour votre stratégie, tout en évitant le piège du keyword stuffing, déjà évoqué. Voici la question principale que se posent les expert·es SEO : « Est-il possible de comprendre de quel sujet traite la page en lisant uniquement la hiérarchie des titres qui y sont présents ? ». Dans l’exemple suivant, il sera compliqué pour Google de comprendre qu’il s’agit de la page d’accueil de la station de ski Mont-Tremblant située au Québec, en consultant uniquement les titres de la page. Hiérarchie de titres HTML imparfaite. Le titre principal est « Réserver maintenant » en h1, suivi de sous-sections comme « Hébergement », « Ski », « Leçons », « Activités » et « Location » en h2. Les titres suivants, intitulés « Vacances d’hiver », « Billets de ski 2024/25 » et « Escapade d’automne », sont structurés avec une balise h6 : cela perturbe la structure, car il manque les niveaux intermédiaires h3, h4, et h5. Enfin, un dernier titre h3 est utilisé pour la section « Ouverture de la saison de ski ». La hiérarchie des titres est aussi importante du point de vue SEO, car Google ne prête pas la même attention aux titres au-delà du niveau 3. Pour plus d’informations, consultez les recommandations officielles de Google pour rédiger des titres (en anglais). Enfin, pour optimiser la hiérarchie de contenu d’une page, demandez-vous pour chaque titre : « Ce titre permet-il de mieux comprendre le thème du contenu principal de la page ? ». Solution Privilégiez les balises HTML natives pour titrer vos contenus : h1, h2, h3, h4, h5, h6. Si votre expert·e SEO vous déconseille d’utiliser une balise native dans des cas très précis : utilisez, ponctuellement, un titre WAI-ARIA avec role="heading" (en anglais) et la propriété aria-level adaptée. Prenons un exemple. Si le pied de page de votre site contient en permanence un bloc permettant de s’abonner à votre newsletter, il ne serait pas pertinent, côté SEO, d’utiliser une balise HTML h2 ou h3 pour titrer ce bloc. En effet, comme ce titre est commun à plusieurs pages, il ne renforcera pas le caractère unique et original de chaque page par rapport aux autres pour Google. Toutefois, pour l’accessibilité, il est essentiel que ce bloc dispose d’un titre : c’est ce qui va permettre aux personnes handicapées utilisant une technologie d’assistance de se repérer dans la page et d’y naviguer plus facilement. C’est pourquoi, dans ce cas précis, avoir recours à un titre WAI-ARIA offre un compromis intéressant pour prioriser l’accessibilité tout en défendant vos intérêts SEO. Ainsi, sur le site d’Access42, nous avons décidé de titrer le bloc « Recevez votre veille sur l’accessibilité numérique par e-mail » avec un titre ARIA de niveau 2. Il n’y a pas de balise HTML h2 : en effet, écrire <p role="heading" aria-level="2">Recevez votre veille sur l’accessibilité numérique par e-mail</p> équivaut, pour les lecteurs d’écran, à écrire <h2>Recevez votre veille sur l’accessibilité numérique par e-mail</h2>, mais sans l’éventuel impact négatif dans Google lié à la redondance du bloc d’une page à l’autre. Attention ! ARIA peut nuire à l’accessibilité si vous l’utilisez mal. Voici des erreurs courantes à éviter absolument : de manière générale, n’utilisez pas les propriétés role="heading" et aria-level sur les balises h1, h2, h3, h4, h5, h6, car il est important de respecter la sémantique des éléments HTML ; n’utilisez pas la propriété role="presentation" sur les balises h1, h2, h3, h4, h5, h6 pour la même raison ; n’utilisez pas aria-hidden="true" sur des textes masqués visuellement avec une classe CSS comme .sr-only, dans l’espoir d’éviter leur indexation par Google. Tout ce que vous allez faire, c’est rendre ces titres inaccessibles pour les personnes handicapées, sans empêcher leur référencement ; n’utilisez pas aria-hidden="true" sur les titres h1, h2, h3, h4, h5, h6 pour empêcher qu’ils ne soient pris en compte par Google : rebelote, celui-ci n’en tiendra pas compte, mais vous empêcherez les personnes handicapées de naviguer dans votre page. Cas où l’accessibilité et le SEO s’opposent 💣 – et où l’accessibilité doit primer ! Citations : ne pas confondre la balise <cite> (accessibilité) et l’attribut cite (SEO) Impact pour l’accessibilité Vous l’avez compris : une des clés de l’accessibilité web, c’est le respect de la sémantique HTML. Tout comme les paragraphes, les listes ou les titres, les citations nécessitent elles aussi d’être correctement structurées 18 . Une distinction doit être faite entre les citations courtes (balise q), et les blocs de citation (balise blockquote). La balise cite (en anglais), quant à elle, peut être utilisée pour citer le titre d’une œuvre dont est tirée une citation, comme dans l’exemple suivant : <p>La citation suivante est un extrait du livre <cite>Survivre au taf. Stratégies d’autodéfense pour personnes minorisées</cite> de Marie Dasylva :</p> <blockquote> <p>Nous avons donc travaillé sur la notion d’excellence, et je le répète : l’excellence, la vraie, est celle qui ne vous tue pas. (…) Déconstruire cette notion commence par le fait d’accepter de devoir s’organiser autour de son handicap au lieu de vouloir montrer qu’on l’a dépassé à tout prix.</p> </blockquote> Ne confondez pas la balise <cite> avec l’attribut cite, qui contient une URI ou une URL menant à la source de la citation. Cet attribut est utilisé uniquement à des fins de SEO, étant donné qu’il est inaccessible aux êtres humains dans la plupart des cas, sauf lorsqu’on utilise le lecteur d’écran JAWS a priori 19 . Impact pour le SEO Les directives de qualité E-E-A-T (Expérience, Expertise, Autorité, Fiabilité) et la mise à jour HCU 20 de Google soulignent l’importance des citations dans le référencement web, bien qu’elles ne soient pas des facteurs de classement directs. Les citations renforcent la crédibilité de votre contenu en l’enrichissant et en soulignant son niveau d’expertise. C’est un outil stratégique qui démontre la fiabilité de votre site et qui, par extension, soutient votre autorité de marque. Cependant, si Google n’accorde aucun traitement spécial au contenu placé dans une balise blockquote, une citation trop longue peut être perçue comme du contenu dupliqué. Cela veut dire que le moteur de recherche peut choisir de ne pas indexer certaines pages considérées comme dupliquées à cause d’un contenu trop similaire à celui d’une autre page. Par ailleurs, l’attribut cite a plusieurs fonctions techniques, mais son impact SEO est limité. S’il peut être lu par les moteurs de recherche, en revanche il ne protège pas contre le contenu dupliqué. Solution Distinguez les citations courtes des blocs de citations grâce aux balises HTML appropriées : q et blockquote. Si besoin, citez l’œuvre dont est extraite chaque citation à l’aide de la balise <cite>. Pour optimiser l’impact SEO d’une citation, utilisez l’attribut cite sur la balise blockquote pour indiquer l’URL dont elle est issue. Privilégiez les sources académiques et les études originales, et veillez à limiter le contenu cité par rapport à votre propre contenu, pour éviter qu’il ne soit considéré comme du contenu dupliqué par Google. Pour en savoir plus, consultez des exemples dans l’article de Rodolphe sur le blog d’Alsacréations : Les citations en HTML avec blockquote, cite et q. Cas qui peuvent créer des obstacles pour l’accessibilité et le SEO 🙈 Liens Liens composites Un lien composite est un bloc entièrement cliquable (balise HTML a) qui regroupe à la fois du texte ainsi qu’un ou plusieurs éléments de type image (balises img, area, object, canvas ou svg). Impact pour l’accessibilité Un lien composite ne pose pas, en soi, de problème pour l’accessibilité, si son intitulé est pertinent, c’est-à-dire s’il permet de comprendre la fonction et la destination du lien 21 . C’est particulièrement important pour les personnes qui présentent une déficience visuelle et qui n’ont pas une vision globale de la page : un intitulé de lien pertinent leur permet de comprendre la fonction de chaque lien, qu’il contienne une image, du texte, ou les deux à la fois. Créer des liens composites est même une bonne pratique pour supprimer la redondance créée par deux liens adjacents menant à la même URL (par exemple un lien image puis un lien texte). En effet, selon les personnes, il peut être plus ou moins facile d’identifier un lien grâce à une icône, ou bien grâce à son intitulé : réunir les deux au sein d’un seul et unique lien permet donc d’améliorer son accessibilité 22 . Maintenant, un lien composite qui contiendrait beaucoup de texte peut nuire à l’expérience utilisateur, notamment pour les personnes utilisant un lecteur d’écran, la mémoire tampon de ces outils étant limitée, la restitution de l’intitulé pourrait être hachée, ce qui peut rendre complexe sa compréhension. Impact pour le SEO Au niveau SEO, les blocs entièrement cliquables empêchent les robots de Google de comprendre l’ancre explicitement sans le contexte des autres éléments du bloc. Il vaut mieux conserver des liens distincts, et étendre la zone de clic si besoin avec CSS et/ou JavaScript. En effet, la longueur du texte d’ancrage n’est pas un critère déterminant pour le SEO, car Google privilégie les intitulés de lien pertinents et naturels qui décrivent la destination du lien. Dans l’article Internal Linking Anchor Texts - Text Variety is Key According to SEO Studies, Daniel Højris Bæk démontre que les sites privilégiant la diversité des textes d’ancrage surpassent leurs concurrents avec un classement SEO moyen de 1,3 (contre 3,5) et un engagement utilisateur supérieur de 50 %, la longueur optimale d’un intitulé de lien se situant autour de 5 mots ou 24 caractères. Pour en savoir plus : Anchor Text : Short vs. Long – What Is Best for SEO? Anchor Text : Types, SEO Implications, and Best Practices The Importance of Anchor Text Diversity Solution Faites attention à la longueur de l’intitulé des liens composites. Si besoin, étendez la zone de clic du lien tout en vous assurant que chaque lien (balise a) reste accessible au clavier. Dans l’article Enhancing The Clickable Area Size, Robin Rendle partage des ressources proposant plusieurs manières de faire. Obfuscation des liens L’obfuscation des liens est une pratique très controversée (pour ne pas dire : d’un autre âge) qui consiste à masquer ou modifier la présentation des liens pour influencer les moteurs de recherche. Cela pose de sérieux problèmes, à la fois pour l’accessibilité et le SEO. Impact pour l’accessibilité Un lien obfusqué pose un problème majeur pour les personnes qui naviguent au clavier, dont des personnes ayant un handicap moteur et/ou un handicap visuel. Voici un exemple de lien obfusqué : <span data-lien="/mentions-legales">Mentions légales</span>. Visuellement, cela peut ressembler à un lien véritable. Techniquement, un script va détecter la cible de ce faux lien, et ouvrir l’URL correspondante lorsque l’utilisatrice cliquera dessus avec sa souris ou son trackpad. Le problème, c’est que le focus ne peut pas être déplacé sur ce faux lien. Non contents de ruiner l’accessibilité de votre page, les faux liens posent d’autres problèmes d’utilisabilité, qui impactent un très large public, comme l’explique très bien Julie Moynat dans l’article Obfuscation de liens en SEO et problèmes d’accessibilité. Impact pour le SEO Google a pris fermement position contre l’obfuscation des liens, qu’il considère comme du « black hat SEO », c’est-à-dire comme une mauvaise pratique qui va à l’encontre de ses recommandations. Selon lui, obfusquer un lien est une tentative de manipulation du référencement. Si vous utilisez ce type de méthode, votre site risque donc d’être lourdement pénalisé dans les résultats de recherche de Google. L’obfuscation des liens est souvent utilisée pour masquer les liens qui mènent à des contenus affiliés. Or, selon John Mueller, représentant de Google (lien en anglais), les liens d’affiliation sont parfaitement acceptables : il est inutile de les masquer ou de les obfusquer sous prétexte de vouloir conserver un bon référencement. Solution N’obfusquez pas les liens, même pas les liens affiliés : n’utilisez pas JavaScript pour modifier la cible des liens sur les actions « onclick » ni d’autres méthodes de type « cloaking » (c’est-à-dire servir un contenu à Google tout en en servant un autre aux êtres humains). Utilisez l’attribut rel="sponsored" sur chaque lien affilié. Cela permet de signaler clairement la nature commerciale du lien aux moteurs de recherche. Cette transparence vous évitera des impacts négatifs sur votre référencement. Si vous n’avez vraiment pas le choix, optez pour une méthode d’obfuscation accessible : elle doit notamment permettre d’atteindre chaque faux lien au clavier, à la voix, ou au moyen d’un lecteur d’écran. Pour en savoir plus, consultez l’idée pour obfusquer les liens de façon accessible proposée par Julie Moynat et Romain Gervois. Attention ! L’obfuscation de liens, même avec une approche plus accessible, est une pratique non seulement dépassée, mais surtout risquée du point de vue du SEO : en effet, Google peut l’interpréter comme une tentative de manipulation qui va à l’encontre de ses directives (en anglais). De plus, si vous utilisez JavaScript pour transformer les faux liens en vrais liens, cela peut ralentir le chargement de la page. Or, Google tient aussi compte de la vitesse de chargement dans son algorithme (en anglais). CQFD. Question subsidiaire : Google tient-il compte de l’accessibilité pour le référencement ? Sundar Pichai, président-directeur général de Google depuis 2015, affirme que l’accessibilité est une valeur fondamentale de l’entreprise, inscrite dans leur mission et appliquée à leurs propres produits (en anglais). Google cherche à prouver cet engagement en partageant, pour l’ensemble de leurs services, les rapports sur leur conformité à l’accessibilité. Cependant, John Mueller, spécialiste des questions de référencement et porte-parole de Google, a adopté une position plus nuancée concernant les sites web tiers dans une interview de 2022 (en anglais) : selon lui, l’accessibilité n’est pas un facteur direct de classement et Google n’a pas pour projet d’en faire un éventuel critère de pertinence. La raison principale tient, selon lui, à l’impossibilité technique de quantifier objectivement l’accessibilité d’un site web. Il n’exclut pas que cela puisse changer à l’avenir en évoquant un système de mesure de l’accessibilité similaire aux Core Web Vitals de Google, un outil permettant de mesurer la qualité d’une page web. Cette possibilité serait d’autant plus pertinente avec l’entrée en vigueur de nouvelles réglementations à travers le monde. Toutefois, à ce jour, Google ne semble pas avoir lancé de véritable projet en ce sens. Capture d’écran 4 : Google does not give a damn about accessibility. But we as humans should. Extrait de la conférence How to ensure that your technical SEO strategy is truly accessible que Billie Geena a donnée à Brighton SEO en 2023. Conclusion L’accessibilité et le SEO ne sont pas fondamentalement incompatibles, mais leur relation reste souvent déséquilibrée. Si de nombreuses bonnes pratiques peuvent servir les deux objectifs, il est essentiel de garder à l’esprit que l’accessibilité doit toujours primer sur le SEO. Cette hiérarchisation est cruciale. En effet, le SEO repose sur des algorithmes dont le principal objectif est la génération de profit, tandis que l’accessibilité vise à répondre aux besoins et droits fondamentaux des personnes en situation de handicap, en leur permettant d’accéder au web et aux outils numériques sans obstacle. C’est pourquoi, affirmer que l’accessibilité améliorerait le SEO est risqué : lorsque les deux entrent en conflit, c’est souvent le SEO qui l’emporte, au détriment des besoins humains. La solution réside dans une approche « accessibility first », similaire au concept « mobile first » (le mobile d’abord). En concevant d’abord pour l’accessibilité, vous créez des interfaces qui répondent aux besoins des utilisateurs et des utilisatrices, tout en anticipant l’évolution des moteurs de recherche vers plus d’inclusivité. Car si Google affirme aujourd’hui que l’accessibilité n’est pas un facteur de classement direct, les nouvelles réglementations et l’évolution des usages le forceront tôt ou tard à intégrer ces principes dans ses outils pour rester pertinent. C’est donc à vous, professionnel·les du numérique, de montrer l’exemple en plaçant l’accessibilité au cœur de tous vos projets numériques. Cela implique de prendre en compte les besoins des personnes handicapées dès la phase de conception, puis d’adapter votre stratégie de référencement en conséquence. La réussite d’une démarche « accessibility first » dépend aussi beaucoup des bons choix de prestataires. Que ce soit pour le design UX/UI, le développement, la production éditoriale, la gestion de projet, l’accessibilité ou le SEO, il est essentiel de travailler avec des expert·es qui comprennent les enjeux de l’accessibilité. En particulier, les expert·es SEO ayant cette sensibilité sauront ajuster leurs recommandations pour allier référencement et accessibilité, contribuant ainsi à la réussite de vos projets web. Remerciements Nous adressons nos plus sincères remerciements aux personnes ayant permis d’enrichir cet article et de vérifier son accessibilité : Audrey Maniez, Luce Carević, Maïa Kopff et Philippe Bouchon d’Access42, ainsi que l’équipe de Paris Web. Search Engine Optimization ↩ Retour au texte 1 ARIA permet de rendre accessibles des composants complexes, en ajoutant de la sémantique et des métadonnées aux contenus HTML (Hypertext Markup Language) pour les lecteurs d’écran. ↩ Retour au texte 2 La norme européenne est également utilisée ailleurs dans le monde, par exemple au Canada, qui en fournit une version HTML. ↩ Retour au texte 3 cf. critère 1.2 du RGAA ↩ Retour au texte 4 Malheureusement, l’option n’est pas disponible dans les éditeurs WYSIWYG de manière native. Si besoin, rapprochez-vous de l’équipe qui développe votre site ou votre thème pour leur faire part de cette demande. ↩ Retour au texte 5 cf. test 1.2.1 du RGAA ↩ Retour au texte 6 cf. cette note en anglais ↩ Retour au texte 7 cf. cette vidéo en anglais : Je me concentrerais davantage sur l’aspect de l’accessibilité que sur l’aspect purement SEO. selon John Mueller, Search Liaison chez Google. Cependant, les bonnes pratiques officielles de Google ne traitent pas des images décoratives : cela peut laisser penser qu’il serait essentiel de renseigner l’alternative de toutes les images, alors que c’est une mauvaise pratique pour l’accessibilité. ↩ Retour au texte 8 cf. critère 6.1 et critère 6.2 du RGAA ↩ Retour au texte 9 Il s’agit du critère de succès 2.4.9 Fonction du lien (lien uniquement) dans les WCAG 2.1, qui est de niveau triple A (AAA). Bien que ce critère faisait l’objet du critère 6.3 dans le RGAA 3, il a été supprimé du RGAA 4. Il est simplement cité dans une de ses annexes. ↩ Retour au texte 10 cf. critère 8.5 et critère 8.6 du RGAA ↩ Retour au texte 11 cf. test 8.6.1 du RGAA ↩ Retour au texte 12 cf. critères 8.3 et critère 8.4 du RGAA ↩ Retour au texte 13 Par exemple : insérer de fausses dates dans le titre pour mieux ressortir dans les résultats de Google ; ou encore décrire des contenus de manière trompeuse dans le titre d’une page, par exemple en incluant une marque populaire de chaussures dans le titre d’une page pour faire référencer une page vendant des chaussures, même si celles-ci ne sont pas de la marque indiquée. ↩ Retour au texte 14 Le « keyword stuffing » consiste à ajouter des mots clés de manière excessive et artificielle dans l’espoir d’améliorer le référencement d’une page. ↩ Retour au texte 15 Par exemple, les nombreuses pages d’un même résultat de recherche. Un ensemble de pages paginées s’appelle une « collection de pages » dans le vocabulaire du RGAA. Cette notion se trouve encore dans le RGAA 4. ↩ Retour au texte 16 cf. critère 9.1 du RGAA ↩ Retour au texte 17 cf. critère 9.4 du RGAA ↩ Retour au texte 18 Seul JAWS restitue la présence de cet attribut dans certains contextes. Pour en savoir plus : Blockquotes in Screen Readers d’Adrian Roselli et HTML5 Accessibility Chops: section elements de Steve Faulkner (cf. le tableau sur JAWS). ↩ Retour au texte 19 E-E-A-T évalue la qualité du contenu, tandis que le HCU favorise le contenu utile pour les utilisateurs et utilisatrices. ↩ Retour au texte 20 cf. test 6.1.3 du RGAA ↩ Retour au texte 21 cf. Technique WCAG H2: Combining adjacent image and text links for the same resource ↩ Retour au texte 22"> <meta name="twitter:image" content="https://feedbot.net/storage/thumbnails/2024_12/ac5b1d08f9771e2f557a8412fde05333-639a385ec0a0e0b441c3c8268c905658.jpg"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/amplitudejs@v5.3.2/dist/amplitude.js"></script> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" href="https://feedbot.net/assets/colors-dark.css?version=1735164589" id="theme"> <link rel="stylesheet" href="https://feedbot.net/assets/style.css?version=1735164749"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css" integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" crossorigin="anonymous"> <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0,user-scalable=no, shrink-to-fit=yes" /> <meta name="apple-mobile-web-app-title" content="Feedbot" /> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="theme-color" content="#11101D" /> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <link rel="apple-touch-startup-image" media="screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="assets/icons/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/iPhone_11__iPhone_XR_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="assets/icons/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="assets/icons/iPhone_14_Pro_Max_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/10.5__iPad_Air_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/11__iPad_Pro__10.5__iPad_Pro_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="assets/icons/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/12.9__iPad_Pro_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="assets/icons/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="assets/icons/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/11__iPad_Pro__10.5__iPad_Pro_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="assets/icons/iPhone_14_Pro_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/10.9__iPad_Air_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/10.2__iPad_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/10.5__iPad_Air_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/12.9__iPad_Pro_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/10.9__iPad_Air_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="assets/icons/iPhone_14_Pro_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="assets/icons/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/8.3__iPad_Mini_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="assets/icons/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="assets/icons/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" href="assets/icons/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="assets/icons/iPhone_14_Pro_Max_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" href="assets/icons/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/10.2__iPad_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" href="assets/icons/8.3__iPad_Mini_landscape.png"> <link rel="apple-touch-startup-image" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" href="assets/icons/iPhone_11__iPhone_XR_portrait.png"> <link rel="manifest" href="./manifest.json"> <link rel="icon" type="image/png" href="https://feedbot.net/assets/icons/icon.png" /> <link rel="apple-touch-icon" href="https://feedbot.net/assets/icons/icon.png" /> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/amplitudejs@{{version-number}}/dist/amplitude.js"></script> <script src="https://feedbot.net/assets/jquery-3.6.3.min.js"></script> <script type="text/javascript"> var website = 'https://feedbot.net'; $(document).ready(function () { $(".article_menu_dots").on("click", function(e){ e.stopPropagation(); var menu_id = $(this).attr("data-id"); $(".article_menu").not("[data-id=" + menu_id + "]").hide(); $(".article_menu[data-id=" + menu_id + "]").toggle(); }); $(document).on("click", function(){ $(".article_menu").hide(); }); $(".article_menu").on("click", function(e){ e.stopPropagation(); }); }); function hideelement($param) { $(".feed_" + $param).hide(); $(".share_" + $param).hide(); } function bookmark($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".bookmark_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if(request){ request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".bookmark_" + $param).hide(); $(".unbookmark_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function unbookmark($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".unbookmark_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".unbookmark_" + $param).hide(); $(".bookmark_" + + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function subscribe($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".subscribe_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".subscribe_" + $param).hide(); $(".suggestion_" + $param).hide(); $(".unsubscribe_" + $param).show(); $(".unsubscribe_" + $param).children("input[name='sub_id']").val(''+response+''); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function unsubscribe($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".unsubscribe_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".unsubscribe_" + $param).hide(); $(".subscribe_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function unshare($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".unshare_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".unshare_" + $param).hide(); $(".share_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function autoshare_on($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".autoshare_on_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".autoshare_on_" + $param).hide(); $(".autoshare_off_" + $param).show(); $(".sharing_options_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function autoshare_off($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".autoshare_off_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".autoshare_off_" + $param).hide(); $(".sharing_options_" + $param).hide(); $(".autoshare_on_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function sharetitle_on($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".sharetitle_on_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".sharetitle_on_" + $param).hide(); $(".sharetitle_off_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function sharetitle_off($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".sharetitle_off_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".sharetitle_off_" + $param).hide(); $(".sharetitle_on_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function sharedescription_on($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".sharedescription_on_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".sharedescription_on_" + $param).hide(); $(".sharedescription_off_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function sharedescription_off($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".sharedescription_off_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".sharedescription_off_" + $param).hide(); $(".sharedescription_on_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function shareimage_on($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".shareimage_on_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".shareimage_on_" + $param).hide(); $(".shareimage_off_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function shareimage_off($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".shareimage_off_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".shareimage_off_" + $param).hide(); $(".shareimage_on_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function is_sensitive_on($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".is_sensitive_on_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".is_sensitive_on_" + $param).hide(); $(".is_sensitive_off_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function is_sensitive_off($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".is_sensitive_off_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".is_sensitive_off_" + $param).hide(); $(".is_sensitive_on_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function sensitive_text($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".sensitive_text_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".sensitive_button_on_" + $param).hide(); $(".sensitive_button_off_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function set_visibility($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".visibility_" + $param).change(function (event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function telegram_on($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".telegram_on_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".telegram_on_" + $param).hide(); $(".telegram_off_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function telegram_off($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".telegram_off_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".telegram_off_" + $param).hide(); $(".telegram_on_" + $param).show(); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function publish() { // Variable to hold request var request; // Bind to the submit event of our form $(".publish").submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Let's disable the inputs for the duration of the Ajax request. // Note: we disable elements AFTER the form data has been serialized. // Disabled form elements will not be serialized. $inputs.prop("disabled", true); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "post", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".publish_button").hide(); $(".publish_area").val(""); $(".publish_button_sent").show(); setTimeout(function(){ $(".publish_button").show(); $(".publish_button_sent").hide(); }, 3000); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); // Callback handler that will be called regardless // if the request failed or succeeded request.always(function (){ // Reenable the inputs $inputs.prop("disabled", false); }); }); } function counter(val) { var len = val.value.length; if(len >= 500){ val.value = val.value.substring(0, 500); } else{ $('.counter').text(500 - len); } } function counter_share(val) { var len = val.value.length; if(len >= 460){ val.value = val.value.substring(0, 460); } else{ $('.counter').text(460 - len); } } function share($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".share_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/share.php", type: "POST", data: serializedData }); // Callback handler that will be called on success request.done(function (data, response, textStatus, jqXHR){ // Log a message to the console $(".publish_popup").empty(); $(".publish_popup").append(data); $(".publish_popup").show(); $('html, body').css({overflow: 'hidden'}); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); }); } function share2($param) { // Variable to hold request var request; // Bind to the submit event of our form $(".share_button").hide(); $(".shared_button").show(); $(".share_content_" + $param).submit(function(event){ // Prevent default posting of form - put here to work in case of errors event.preventDefault(); // Abort any pending request if (request) { request.abort(); } // setup some local variables var $form = $(this); // Let's select and cache all the fields var $inputs = $form.find("input, select, button, textarea"); // Serialize the data in the form var serializedData = $form.serialize(); // Fire off the request to /form.php request = $.ajax({ url: website + "/includes/action.php", type: "POST", data: serializedData }); // Callback handler that will be called on success request.done(function (response, textStatus, jqXHR){ // Log a message to the console $(".share_" + $param).hide(); $(".unshare_" + $param).show(); $(".unshare_" + $param).children("input[name='status_id']").val(''+response+''); $(".publish_popup").hide(); $(".publish_popup").empty(); $('html, body').css({overflow: ''}); }); // Callback handler that will be called on failure request.fail(function (jqXHR, textStatus, errorThrown){ // Log the error to the console console.error( "The following error occurred: "+ textStatus, errorThrown ); }); }); } function hide_publish(back_url) { var back = back_url; $(".publish_popup").hide(); $(".publish_popup").empty(); $('html, body').css({overflow: ''}); window.history.pushState("data", "Title", back); Amplitude.stop(); } function story(media_id){ var new_url = website + "/story/" + media_id; $.ajax({ url: website + '/includes/stories.php?media=' + media_id, type: "GET", beforeSend: function(){ $('.ajax-load').show(); $(".publish_popup").empty(); } }).done(function(data){ $('.ajax-load').hide(); $(".publish_popup").append(data); window.history.pushState("data", "Title", new_url); $(".publish_popup").show(); $('html, body').css({overflow: 'hidden'}); }).fail(function(jqXHR, ajaxOptions, thrownError){ }); } function podcast(id){ var new_url = website + "/podcast/" + id; $.ajax({ url: website + '/includes/podcast.php?id=' + id, type: "GET", beforeSend: function(){ $('.ajax-load').show(); $(".publish_popup").empty(); } }).done(function(data){ $('.ajax-load').hide(); $(".publish_popup").append(data); window.history.pushState("data", "Title", new_url); $(".publish_popup").show(); $('html, body').css({overflow: 'hidden'}); }).fail(function(jqXHR, ajaxOptions, thrownError){ }); } function dark_mode() { $('#theme').attr('href', 'https://feedbot.net/assets/colors-dark.css'); $('.light_mode').show(); $('.dark_mode').hide(); document.cookie = "theme=dark; path=/; max-age=" + 30*24*60*60; } function light_mode() { $('#theme').attr('href', 'https://feedbot.net/assets/colors-light.css'); $('.light_mode').hide(); $('.dark_mode').show(); document.cookie = "theme=light; path=/; max-age=" + 30*24*60*60; $(".video_cinema").hide(); } function constructFeed(json, getJSON = true){ return new Promise((resolve, reject) => { var constructFeed; var num_content = json_content.length - 1; $.each(json, function(index, value){ if(json_content[index]){ var feed_id = json_content[index]["feed_id"]; if(json_content[index]["youtube_id"]){ var youtube_id = json_content[index]["youtube_id"]; } else{ var youtube_id = ""; } if(json_content[index]["peertube_id"]){ var peertube_id = json_content[index]["peertube_id"]; } else{ var peertube_id = ""; } if(json_content[index]["description"]){ var description = json_content[index]["description"]; description = description.replace(/\\n/g, '<br>'); } constructFeed += ` <div class="content-home" style="padding-top: 15px; padding-bottom: 0px;" id="` + json_content[index]["date"] + `"> <div style="height:75px;">`; if(json_feeds[feed_id]["is_subscribed"] == 1){ constructFeed += ` <form class="follow-button subscribe_` + json_content[index]["feed_id"] + `" style="display:none;" title="SUBSCRIBE_TO ` + json_feeds[feed_id]["name"] + `"> <input type="hidden" name="action" value="subscribe"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <button onclick="subscribe(` + json_content[index]["feed_id"] + `)"><i class="fa fa-rss" aria-hidden="true"></i></button> </form> <form class="follow-button-subscribed unsubscribe_` + json_content[index]["feed_id"] + `" title="UNSUBSCRIBE_TO ` + json_feeds[feed_id]["name"] + `"> <input type="hidden" name="action" value="unsuscribe"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <input type="hidden" name="sub_id" value="` + json_feeds[feed_id]["sub_id"] + `"> <button onclick="unsubscribe(` + json_content[index]["feed_id"] + `)"> <i class="fa fa-rss" aria-hidden="true"></i> </button> </form>`; } else{ constructFeed += ` <form class="follow-button subscribe_` + json_content[index]["feed_id"] + `" title="SUBSCRIBE_TO ` + json_feeds[feed_id]["name"] + `"> <input type="hidden" name="action" value="subscribe"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <button onclick="subscribe(` + json_content[index]["feed_id"] + `)"><i class="fa fa-rss" aria-hidden="true"></i></button> </form> <form class="follow-button-subscribed unsubscribe_` + json_content[index]["feed_id"] + `" style="display:none;" title="UNSUBSCRIBE_TO ` + json_feeds[feed_id]["name"] + `"> <input type="hidden" name="action" value="unsuscribe"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <input type="hidden" name="sub_id" value="` + json_feeds[feed_id]["sub_id"] + `"> <button onclick="unsubscribe(` + json_content[index]["feed_id"] + `)"> <i class="fa fa-rss" aria-hidden="true"></i> </button> </form>`; } constructFeed += ` <div style="width: 60px; aspect-ratio: 1 / 1; background-image: url(` + json_feeds[feed_id]["avatar"] + `); background-size: cover; background-position: center; border-radius: 50%; float:left; overflow: hidden;"> <a href="https://feedbot.net/feed/` + json_content[index]["feed_id"] + `" style="display:block; width:100%; height:100%;"></a> </div> <div style="margin-left: 74px; padding-top: 13px; line-height: 16px;"> <div style="display: flex; grid-gap: 4px;"> <a href="https://feedbot.net/feed/` + json_content[index]["feed_id"] + `" style="color:var(--feedbot-title); font-weight: bold; text-decoration: none; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;" title="` + json_feeds[feed_id]["name"] + `">` + json_feeds[feed_id]["name"] +`</a>`; if(json_content[index]["youtube_id"]){ constructFeed += ` <img src="https://feedbot.net/assets/youtube.png" alt="YouTube channel" style="width:20px; margin-left: 4px; margin-bottom: 1px; vertical-align: middle;" />`; } if(json_content[index]["peertube_id"]){ constructFeed += ` <img src="https://feedbot.net/assets/peertube.png" alt="PeerTube channel" style="width:14px; margin-left: 5px; vertical-align: middle;" />`; } constructFeed += ` </div>`; if(!json_content[index]["youtube_id"] && !json_content[index]["peertube_id"]){ constructFeed += ` <a href="https://feedbot.net/feed/` + json_content[index]["feed_id"] + `/` + json_content[index]["article_id"] + `" class="relative_date" style="font-size: 12px; text-decoration: none; color: var(--feedbot-text);" data-timestamp="` + json_content[index]["date"] + `">` + relativedate(json_content[index]["date"]) + `</a>`; } else if(json_content[index]["youtube_id"] && !json_content[index]["peertube_id"]){ constructFeed += ` <a href="https://feedbot.net/watch/` + json_content[index]["youtube_id"] + `" class="relative_date" style="font-size: 12px; text-decoration: none; color: var(--feedbot-text);" data-timestamp="` + json_content[index]["date"] + `">` + relativedate(json_content[index]["date"]) + `</a>`; } else if(!json_content[index]["youtube_id"] && json_content[index]["peertube_id"]){ constructFeed += ` <a href="https://feedbot.net/watch/` + json_content[index]["peertube_id"] + `" class="relative_date" style="font-size: 12px; text-decoration: none; color: var(--feedbot-text);" data-timestamp="` + json_content[index]["date"] + `">` + relativedate(json_content[index]["date"]) + `</a>`; } constructFeed += ` </div> </div>`; if(json_content[index]["description"]){ constructFeed += ` <div class="description" id="description-` + json_content[index]["article_id"] + `" data-article-id="` + json_content[index]["article_id"] + `">` + description + `</div> <div style="height:20px;"> </div>`; } constructFeed +=` <div class="timeline-thumbnail"> <div class="timeline-thumbnail-content" style="background-image: url(` + json_content[index]["thumbnail"] + `);">`; if(json_content[index]["youtube_id"]){ constructFeed += ` <a href="https://feedbot.net/watch/` + json_content[index]["youtube_id"] + `" style="display:block; width:100%; height:100%;"></a> </div> <div class="timeline-title"> <a href="https://feedbot.net/watch/` + json_content[index]["youtube_id"] + `" style="text-decoration: none;"><span style="color:var(--feedbot-gray); font-size: 12px; text-transform: uppercase;">` + json_content[index]["media_url"] + `</span><br /><span style="color:var(--feedbot-title); font-weight:bold;">` + json_content[index]["title"] + `</span></a> </div> </div>`; } if(json_content[index]["peertube_id"]){ constructFeed += ` <a href="https://feedbot.net/watch/` + json_content[index]["peertube_id"] + `" style="display:block; width:100%; height:100%;"></a> </div> <div class="timeline-title"> <a href="https://feedbot.net/watch/` + json_content[index]["peertube_id"] + `" style="text-decoration: none;"><span style="color:var(--feedbot-gray); font-size: 12px; text-transform: uppercase;">` + json_content[index]["media_url"] + `</span><br /><span style="color:var(--feedbot-title); font-weight:bold;">` + json_content[index]["title"] + `</span></a> </div> </div>`; } if(!json_content[index]["youtube_id"] && !json_content[index]["peertube_id"]){ constructFeed += ` <a href="` + json_content[index]["url"] + `" target="_blank" style="display:block; width:100%; height:100%;"></a> </div> <div class="timeline-title"> <a href="` + json_content[index]["url"] + `" target="_blank" title="` + json_content[index]["title"] + `" style="text-decoration: none;"><span style="color:var(--feedbot-gray); font-size: 12px; text-transform: uppercase;">` + json_content[index]["media_url"] + `</span><br /><span style="color:var(--feedbot-title); font-weight:bold; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;">` + json_content[index]["title"] + `</span></a> </div> </div>`; } constructFeed += ` <div style="position:relative; display: block; height: 28px;"> <div class="timeline-buttons-left">`; if(json_content[index]["is_shared"] !== "1"){ constructFeed += ` <form class="timeline-buttons share_` + json_content[index]["article_id"] + `"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <input type="hidden" name="site_id" value="` + json_content[index]["site_id"] + `"> <input type="hidden" name="messagetitle" value="` + json_content[index]["title"] + `"> <input type="hidden" name="message" value="` + json_content[index]["description_link"] + `"> <input type="hidden" name="url" value="` + json_content[index]["url"] + `"> <input type="hidden" name="peertubeid" value="` + peertube_id + `"> <input type="hidden" name="youtubeid" value="` + youtube_id + `"> <input type="hidden" name="thumbnail" value="` + json_content[index]["thumbnail"] + `"> <button onclick="share(` + json_content[index]["article_id"] + `)" class="timeline-buttons" title="SHARE"> <i class="fa fa-retweet" aria-hidden="true" style="margin-right: 5px;"></i> SHARE</span> </button> </form> <form class="timeline-buttons unshare_` + json_content[index]["article_id"] + `" style="display: none;"> <input type="hidden" name="action" value="delete_status"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <input type="hidden" name="status_id" value=""> <button onclick="unshare(` + json_content[index]["article_id"] + `)" type="submit" class="timeline-buttons" title="SHARED : ` + json_content[index]["relative_date"] + `" style="color:var(--feedbot-purple);"> <i class="fa fa-retweet" aria-hidden="true" style="margin-right: 5px;"></i> SHARED</span> </button> </form>`; } else{ constructFeed += ` <form class="timeline-buttons share_` + json_content[index]["article_id"] + `" style="display: none;"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <input type="hidden" name="site_id" value="` + json_content[index]["site_id"] + `"> <input type="hidden" name="messagetitle" value="` + json_content[index]["title"] + `"> <input type="hidden" name="message" value="` + json_content[index]["description_link"] + `"> <input type="hidden" name="url" value="` + json_content[index]["url"] + `"> <input type="hidden" name="peertubeid" value="` + peertube_id + `"> <input type="hidden" name="youtubeid" value="` + youtube_id + `"> <input type="hidden" name="thumbnail" value="` + json_content[index]["thumbnail"] + `"> <button onclick="share(` + json_content[index]["article_id"] + `)" class="timeline-buttons" title="SHARE"> <i class="fa fa-retweet" aria-hidden="true" style="margin-right: 5px;"></i> SHARE</span> </button> </form> <form class="timeline-buttons unshare_` + json_content[index]["article_id"] + `"> <input type="hidden" name="action" value="delete_status"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <input type="hidden" name="status_id" value="` + json_content[index]["post_id"] + `"> <button onclick="unshare(` + json_content[index]["article_id"] + `)" type="submit" class="timeline-buttons" title="SHARED : ` + json_content[index]["relative_date"] + `" style="color:var(--feedbot-purple);"> <i class="fa fa-retweet" aria-hidden="true" style="margin-right: 5px;"></i> SHARED</span> </button> </form>`; } constructFeed += ` </div> <div class="timeline-buttons-right">`; if(json_content[index]["bookmarked"] !== "1"){ constructFeed += ` <form class="timeline-buttons bookmark_` + json_content[index]["article_id"] + `" class="timeline-buttons-div"> <input type="hidden" name="action" value="bookmark"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <input type="hidden" name="site_id" value="` + json_content[index]["site_id"] + `"> <button onclick="bookmark(` + json_content[index]["article_id"] + `)" class="timeline-buttons" title="READ_LATER"> <i class="fa fa-bookmark" aria-hidden="true" style="margin-right: 5px;"></i> READ_LATER </button> </form> <form class="timeline-buttons unbookmark_` + json_content[index]["article_id"] + `" style="display:none;" class="timeline-buttons-div"> <input type="hidden" name="action" value="delete_bookmark"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <button onclick="unbookmark(` + json_content[index]["article_id"] + `)" class="timeline-buttons" title="READ_LATER" style="color:red;"> <i class="fa fa-bookmark" aria-hidden="true" style="margin-right: 5px;"></i> READ_LATER </button> </form>`; } else{ constructFeed += ` <form class="timeline-buttons bookmark_` + json_content[index]["article_id"] + `" style="display:none;" class="timeline-buttons-div"> <input type="hidden" name="action" value="bookmark"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <input type="hidden" name="feed_id" value="` + json_content[index]["feed_id"] + `"> <input type="hidden" name="site_id" value="` + json_content[index]["site_id"] + `"> <button onclick="bookmark(` + json_content[index]["article_id"] + `)" class="timeline-buttons" title="READ_LATER"> <i class="fa fa-bookmark" aria-hidden="true" style="margin-right: 5px;"></i> READ_LATER </button> </form> <form class="timeline-buttons unbookmark_` + json_content[index]["article_id"] + `" method="post" class="timeline-buttons-div"> <input type="hidden" name="action" value="delete_bookmark"> <input type="hidden" name="article_id" value="` + json_content[index]["article_id"] + `"> <button onclick="unbookmark(` + json_content[index]["article_id"] + `)" class="timeline-buttons" title="UNBOOKMARKS" style="color:red;"> <i class="fa fa-bookmark" aria-hidden="true" style="margin-right: 5px;"></i> READ_LATER </button> </form>`; } constructFeed += ` </div> </div> </div>`; } }); constructFeed = constructFeed.replace("undefined", ''); if(getJSON){ $.ajax({ url: "https://feedbot.net" + "/includes/infinite.php?last_id=" + json_content[num_content]["date"] + "&feed=" + feed_id + "&page=" + page + "&search=" + search, type: "GET", dataType: 'json', beforeSend: function(){ $("#post-data").append(` <div class="wait" style="display: none; align-items: center; justify-content: center; padding: 20px;"> <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"> <circle cx="12" cy="3" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle0" attributeName="r" begin="0;svgSpinners6DotsScaleMiddle2.end-0.5s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="16.5" cy="4.21" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle1" attributeName="r" begin="svgSpinners6DotsScaleMiddle0.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="7.5" cy="4.21" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle2" attributeName="r" begin="svgSpinners6DotsScaleMiddle4.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="19.79" cy="7.5" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle3" attributeName="r" begin="svgSpinners6DotsScaleMiddle1.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="4.21" cy="7.5" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle4" attributeName="r" begin="svgSpinners6DotsScaleMiddle6.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="21" cy="12" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle5" attributeName="r" begin="svgSpinners6DotsScaleMiddle3.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="3" cy="12" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle6" attributeName="r" begin="svgSpinners6DotsScaleMiddle8.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="19.79" cy="16.5" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle7" attributeName="r" begin="svgSpinners6DotsScaleMiddle5.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="4.21" cy="16.5" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle8" attributeName="r" begin="svgSpinners6DotsScaleMiddlea.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="16.5" cy="19.79" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddle9" attributeName="r" begin="svgSpinners6DotsScaleMiddle7.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="7.5" cy="19.79" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddlea" attributeName="r" begin="svgSpinners6DotsScaleMiddleb.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> <circle cx="12" cy="21" r="0" fill="currentColor"> <animate id="svgSpinners6DotsScaleMiddleb" attributeName="r" begin="svgSpinners6DotsScaleMiddle9.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".27,.42,.37,.99;.53,0,.61,.73" values="0;2;0"/> </circle> </svg> </div>`); }, success: function(response) { if(response){ json_content = response["content"]; json_feeds = response["feeds"]; } else{ json_content = false; } $(".wait").remove(); isActive = false; resolve(constructFeed); }, }) } else{ resolve(constructFeed); } }); } function get_sticky_position($elem){ event_scroll_position = $(window).scrollTop(); window_width = $(window).width(); var window_height = $(window).height(); var elem_height = $elem.outerHeight(true); if(window_width > 1010){ if(elem_height > window_height){ if(event_scroll_position >= scroll_position){ max_sticky_position = $(window).height() - elem_height - 18; var scroll_difference = scroll_position - event_scroll_position; sticky_position = sticky_position + scroll_difference; if(sticky_position <= max_sticky_position){ sticky_position = max_sticky_position; } $elem.css("top", sticky_position); } else{ var scroll_difference = scroll_position - event_scroll_position; sticky_position = sticky_position + scroll_difference; if(sticky_position > 18){ sticky_position = 18; } $elem.css("top", sticky_position); } } else{ sticky_position = 18; $elem.css("top", sticky_position); } } else{ $elem.css("top", "unset"); } scroll_position = $(window).scrollTop(); } function relativedate(ts){ // Vérifie si le timestamp est un entier, sinon essaie de le convertir if (!/^\d+$/.test(ts)) { ts = Date.parse(ts) / 1000; // Convertit en secondes } // Si ts est invalide après conversion if (isNaN(ts)) return "Invalid date"; let diff = Math.floor(Date.now() / 1000) - ts; // Différence en secondes if (diff === 0) { return "JUST_NOW"; } else if (diff > 0) { let day_diff = Math.floor(diff / 86400); if (day_diff === 0) { if (diff < 60) return "JUST_NOW"; if (diff < 120) return "ONE_MINUTE_AGO"; if (diff < 3600) return `THERE_IS ${Math.floor(diff / 60)} MINUTESAGO`; if (diff < 7200) return "ONE_HOUR_AGO"; if (diff < 86400) return `THERE_IS ${Math.floor(diff / 3600)} HOURSAGO`; } if (day_diff === 1) return "YESTERDAY"; if (day_diff < 7) return `THERE_IS ${day_diff} DAYSAGO`; if (day_diff < 31) return `THERE_IS ${Math.ceil(day_diff / 7)} WEEKSAGO`; if (day_diff < 60) return "LAST_MONTH"; return new Date(ts * 1000).toLocaleString(); } else { diff = Math.abs(diff); // Différence positive pour le futur let day_diff = Math.floor(diff / 86400); if (day_diff === 0) { if (diff < 120) return "In a minute"; if (diff < 3600) return `In ${Math.floor(diff / 60)} minutes`; if (diff < 7200) return "In an hour"; if (diff < 86400) return `In ${Math.floor(diff / 3600)} hours`; } if (day_diff === 1) return "Tomorrow"; if (day_diff < 4) return new Date(ts * 1000).toLocaleDateString("en-US", { weekday: 'long' }); if (day_diff < 7 + (7 - new Date().getDay())) return "Next week"; if (Math.ceil(day_diff / 7) < 4) return `In ${Math.ceil(day_diff / 7)} weeks`; if (new Date(ts * 1000).getMonth() === new Date().getMonth() + 1) return "Next month"; return new Date(ts * 1000).toLocaleString("en-US", { month: 'long', year: 'numeric' }); } } function refresh_dates(){ $("#post-data").find('.relative_date').each(function(){ var timestamp = $(this).attr("data-timestamp"); $(this).html(relativedate(timestamp)); }); } function checkNewContent(){ $.ajax({ url: "https://feedbot.net/includes/action.php", type: "POST", data: { action: "check_new_content", from: page, first_id: first_id }, success: function(response){ if(response == "yes"){ $("#new_content").css("display", "flex"); refresh_content = false; } } }); refresh_dates(); } function getNewContent(){ $.ajax({ url: "https://feedbot.net/includes/action.php", type: "POST", data: { action: "get_new_content", from: page, first_id: first_id }, dataType: "json", success: function(response){ var temp_json_content = json_content; var temp_json_feeds = json_feeds; if(response){ json_content = response.content; json_feeds = response.feeds; if(json_content){ constructFeed(json_content, getJSON = false).then((construct) => { var construct = construct; json_content = temp_json_content; json_feeds = temp_json_feeds; $(construct).insertAfter($("#new_content")); $("#post-data").find('.description:not([data-loaded]').each(function(){ linesCount(this); $(this).attr('data-loaded', 'true'); }); topFunction(); first_id = $(".content-home:first").attr("id"); $("#new_content").hide(); refresh_content = true; }); } } } }); } </script> <script type="text/javascript"> var window_width = $(window).width(); var scroll_position = $(window).scrollTop(); var sticky_position = 18; var search; var json_content; var json_feeds; console.log(json_content); </script> </head> <style type="text/css"> .lists_list{ max-height: 300px; display: flex; flex-direction: column; overflow: scroll; scrollbar-width: thin; } .lists_list_item{ display: flex; align-items: center; grid-gap: 10px; } .lists_popup{ position: fixed; display: none; align-items: center; justify-content: center; height: 100dvh; z-index: 3; background-color: #0F0F0FEE; backdrop-filter: blur(8px); } .lists_popup_list{ padding: 10px; width: 90%; max-width: 500px; display: flex; flex-direction: column; grid-gap: 15px; background-color: var(--feedbot-content-background); border-radius: 12px; } .create_list{ background-color: var(--feedbot-background); } .create_list, .lists_list_item{ cursor: pointer; padding: 10px; border-radius: 8px; } .create_list:hover, .lists_list_item:hover{ background-color: var(--feedbot-thumbnails-background); } .create_list_button{ display: flex; align-items: center; justify-content: center; grid-gap: 10px; } .create_list_form{ display: none; align-items: center; grid-gap: 10px; } </style> <body ontouchstart=""> <!-- Sidebar source https://www.codinglabweb.com/2021/04/responsive-side-navigation-bar-in-html.html --> <div class="sidebar "> <div class="logo-details" style="cursor: pointer;"> <img src="https://feedbot.net/assets/feedbot-logo.png" class="icon" style="height:28px; margin:8px;" onclick="window.location='https://feedbot.net/'" title="Feedbot"/> <div class="logo_name" onclick="window.location='https://feedbot.net/'" title="Feedbot"> Feedbot </div> <i class="fa fa-bars" aria-hidden="true" id="btn"></i> </div> <ul class="nav-list"> <li> <a href="https://feedbot.net/global"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 15 15"> <path fill="currentColor" fill-rule="evenodd" d="M0 7.5a7.5 7.5 0 1 1 15 0a7.5 7.5 0 0 1-15 0Zm1 0a6.502 6.502 0 0 1 5.527-6.428L7 1.31v.382l-.724.362a.5.5 0 0 0-.053.863l1.5 1A.5.5 0 0 0 8 4h.5a.5.5 0 0 0 .5-.5V3h.5v.5a.5.5 0 0 0 .146.354l.354.353v.586L9.793 5h-.675l-1.894-.947a.5.5 0 0 0-.448 0l-.894.447H4.5a.5.5 0 0 0-.485.379l-.5 2a.5.5 0 0 0 .131.475l1.5 1.5a.5.5 0 0 0 .13.093L6 9.31v1.19a.5.5 0 0 0 .146.354l.354.353V12a.5.5 0 0 0 .053.224l.5 1a.5.5 0 0 0 .447.276h1a.5.5 0 0 0 .416-.223l1-1.5a.5.5 0 0 0 .031-.053l.5-1a.5.5 0 0 0 .053-.224v-.833L11.9 7.8a.5.5 0 0 0 .047-.524L11.81 7h.691a.5.5 0 0 0 .5-.5V6h.826A6.5 6.5 0 1 1 1 7.5Z" clip-rule="evenodd"/> </svg> <span class="links_name">GLOBAL_FEED</span> </a> <span class="tooltip">GLOBAL_FEED</span> </li> <li style="display:none;" class="dark_mode"> <a onclick="dark_mode()"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 15 15"> <path fill="currentColor" d="M7.707.003a.5.5 0 0 0-.375.846a6 6 0 0 1-5.569 10.024a.5.5 0 0 0-.519.765A7.5 7.5 0 1 0 7.707.003Z"/> </svg> <span class="links_name">Mode sombre</span> </a> <span class="tooltip" style="margin-top: 7px;">Mode sombre</span> </li> <li class="light_mode"> <a onclick="light_mode()"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 15 15"> <path fill="currentColor" d="M8 2V0H7v2h1Zm-4.793.498L1.5.792L.793 1.5L2.5 3.206l.707-.708Zm9.293.708L14.207 1.5L13.5.792l-1.707 1.706l.707.708Zm-5 .791a3.499 3.499 0 1 0 0 6.996a3.499 3.499 0 1 0 0-6.996ZM2 6.995H0v1h2v-1Zm13 0h-2v1h2v-1ZM1.5 14.199l1.707-1.707l-.707-.707l-1.707 1.706l.707.708Zm12.707-.708L12.5 11.785l-.707.707L13.5 14.2l.707-.708ZM8 14.99v-1.998H7v1.999h1Z"/> </svg> <span class="links_name">Mode clair</span> </a> <span class="tooltip" style="margin-top: 7px;">Mode clair</span> </li> <li class="profile"> <div class="profile-details"> <img src="https://feedbot.net/assets/avatar-nopreview.png" alt="profileImg" onclick="location.href='https://feedbot.net/?p=signin';" style="cursor: pointer;"> <div class="name_job"> <div class="name"> GUEST </div> <div class="job"> </div> </div> </div> <i class="fa fa-sign-in" aria-hidden="true" id="log_in" onclick="location.href='https://feedbot.net/?p=signin';" style="cursor: pointer;"></i> </li> </ul> </div> <script> $(document).on("click", "#btn", function(){ $(".sidebar").toggleClass("open"); menuBtnChange(); }); $(document).on("click", ".bx-search", function(){ $(".sidebar").toggleClass("open"); menuBtnChange(); }); function menuBtnChange(){ window_width = $(window).width(); var content_width = $(".home-section").width(); if($(".sidebar").hasClass("open")){ $("#btn").removeClass("fa-bars").addClass("fa-align-right"); document.cookie = "open=open; path=/; max-age=" + 30 * 24 * 60 * 60; if(window_width <= 720){ $(".home-section").css("width", content_width); $("body").css("overflow-x", "hidden"); } } else{ $("#btn").removeClass("fa-align-right").addClass("fa-bars"); document.cookie = "open=close; path=/; max-age=" + 30 * 24 * 60 * 60; $(".home-section").css("width", ""); $("body").css("overflow-x", ""); } } $(document).on("submit", "#feedbot_search", function(e){ e.preventDefault; e.stopImmediatePropagation; var search = encodeURIComponent($("#feedbot_search input[type=text]").val()); console.log("https://feedbot.net/search/" + search); document.location.href="https://feedbot.net/search/" + search; return false; }); </script> <div class="publish_popup zoomin" style="display: none;"> </div> <section class="home-section"> <div class="lists_popup"> <div class="lists_popup_list"> <div class="title" style="height: unset; padding-top: unset; padding: 15px; margin-bottom: unset; font-size: 24px;"> <div style="display: flex; align-items: center; justify-content: center; grid-gap: 10px;"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" style="min-width: 24px; min-height: 24px;" viewBox="0 0 24 24"> <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18 18h2m2 0h-2m0 0v-2m0 2v2M2 11h18M2 17h12M2 5h18"/> </svg> Ajouter à une liste </div> </div> <div class="lists_list"> </div> <div class="create_list"> <div class="create_list_button" tabindex="0"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 15 15"> <path fill="currentColor" fill-rule="evenodd" d="M7 7V1h1v6h6v1H8v6H7V8H1V7h6Z" clip-rule="evenodd"/> </svg> Créer une liste </div> <div class="create_list_form" tabindex="0"> <input id="new_list_title" type="text" placeholder="Titre de la liste..." style="margin: unset; border-radius: 6px;"> <button style="padding: 10px 14px; margin-top: unset;"> Créer </button> </div> <input type="hidden" name="list_article"> </div> </div> </div> <script type="text/javascript"> $(document).on("click", function(e){ if (!$(e.target).closest(".lists_popup_list").length){ close_lists_popup(); } }); $(document).on("keydown", function(e){ if(e.key === "Escape" || e.keyCode === 27) { close_lists_popup(); } }); $(document).on("click", ".create_list_button", function(){ $(".create_list_button").hide(); $(".create_list_form").css("display", "flex"); $("#new_list_title").focus(); }); $(document).on("click", ".lists_list_item", function(e){ var list_id = $(this).attr("data-list-id"); var isChecked = $(".lists_list_item input[data-list-id='" + list_id + "']").prop("checked"); if(isChecked){ $(".lists_list_item input[data-list-id='" + list_id + "']").prop("checked", false).trigger("change"); } else{ $(".lists_list_item input[data-list-id='" + list_id + "']").prop("checked", true).trigger("change"); } }); $(document).on("click", ".create_list_form button", function(){ var list_title = $("#new_list_title").val(); create_playlist(list_title); }); $(document).on("keydown", "#new_list_title", function(e){ if(e.key === "Enter" || e.keyCode === 13){ var list_title = $("#new_list_title").val(); create_playlist(list_title); } }); $(document).on("change", "input[data-list-id]", function(){ if($(this).attr("data-update") == "false"){ return; } var article_id = $("input[name='list_article']").val(); var list_id = $(this).attr("data-list-id"); var isChecked = $(this).prop("checked"); $.ajax({ url: "https://feedbot.net/includes/action.php", method: "POST", data: { action: "add_to_list", article_id: article_id, list_id: list_id, checked: isChecked ? 1 : 0 }, success: function(response){ if(response == "success"){ console.log(list_id); var list_item = $(".lists_list_item[data-list-id='" + list_id + "']").detach(); $(".lists_list").prepend(list_item); } } }); }); function close_lists_popup(){ $("body").css("overflow-y", ""); $(".lists_popup").hide(); $("#new_list_title").val(""); $("input[name='list_article']").val(""); $(".create_list_form").hide(); $(".create_list_button").css("display", "flex"); $(".lists_list_item input[type='checkbox']").each(function() { $(this).prop("checked", false); $(this).attr("data-update", "false"); }); } function create_playlist(title){ var article_id = $("input[name='list_article']").val(); $.ajax({ url: "https://feedbot.net/includes/action.php", type: "POST", data: { action: "create_list", title: title }, dataType: "json", success: function(response){ if(response.response == "success"){ var list_id = response.id; var list_title = response.title; $.ajax({ url: "https://feedbot.net/includes/action.php", method: "POST", data: { action: "add_to_list", article_id: article_id, list_id: list_id, checked: 1 }, success: function(response){ $(".lists_list").prepend(` <div class="lists_list_item" tabindex="0" data-list-id="` + list_id + `"> <div> <input type="checkbox" data-list-id="` + list_id + `" data-update="true" checked> </div> <div> ` + list_title + ` </div> </div>`); $(".create_list_form").hide(); $("#new_list_title").val(""); $(".create_list_button").css("display", "flex"); } }); } } }); } </script> <button onclick="topFunction();" id="myBtn" title="Go to top"> <i class="fa fa-arrow-up" aria-hidden="true"></i> </button> <div class="banner-contenair"> <div class="single-feed-banner" style="background-image: url(https://feedbot.net/storage/thumbnails/2024_12/ac5b1d08f9771e2f557a8412fde05333-639a385ec0a0e0b441c3c8268c905658.jpg);"> </div> <div class="single-feed-avatar" style="background-image: url(https://feedbot.net/storage/icons/5fa8a2983f95221c72e0731a8b74ae38158897b7.svg);"> </div> <div class="single-feed-title"> <p style="font-size:20px; font-weight:bold; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;">24 jours de web </p> <span style="font-size: 16px;">www.24joursdeweb.fr</span> </div> <div class="single-feed-subscribe"> <form action="./?p=signin" method="post"> <button type="submit" title="SUBSCRIBE_TO 24 jours de web"/> <span><i class="fa fa-rss" aria-hidden="true" style="margin-right: 5px;"></i> SUBSCRIBE</span> </button> </form> </div> </div> <div class="contenair-home"> <div id="post-data"> <div class="content-home" style="padding-top: 15px; padding-bottom: 0px;" id="1734649200"> <div style="height:75px;"> <form action="https://feedbot.net" class="follow-button subscribe_364" method="post" title="SUBSCRIBE_TO 24 jours de web"> <button type="submit"><i class="fa fa-rss" aria-hidden="true"></i></button> </form> <div style="width: 60px; aspect-ratio: 1 / 1; background-image: url(https://feedbot.net/storage/icons/5fa8a2983f95221c72e0731a8b74ae38158897b7.svg); background-size: cover; background-position: center; border-radius: 50%; float:left; overflow: hidden;"> <a href="https://feedbot.net/feed/364" style="display:block; width:100%; height:100%;"></a> </div> <div style="margin-left: 74px; padding-top: 13px; line-height: 16px;"> <a href="https://feedbot.net/feed/364" style="color:var(--feedbot-title); font-weight: bold; text-decoration: none; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;">24 jours de web</a> <a href="https://feedbot.net/feed/364/765219" style="font-size: 12px; text-decoration: none; color: var(--feedbot-text);">THERE_IS1WEEKSAGO</a> </div> </div> <div class="description" data-article-id="765219" id="description-765219">Google est souvent qualifié de « premier utilisateur aveugle du Web ». Cette idée mérite d’être déconstruite, car contrairement aux personnes en situation de handicap qui cherchent simplement à consulter et à publier des contenus numériques, les robots de Google œuvrent uniquement pour la performance commerciale. Comme chien et chat ? Photo de Alexander Grey pour Pexels. Malgré tout, concilier accessibilité et référencement (SEO 1 ) est possible à condition de prioriser les besoins des êtres humains par rapport aux algorithmes des moteurs de recherche, qu’ils soient géants comme Google et Bing, ou alternatifs comme Ecosia et DuckDuckGo. Ce constat nous pousse à mêler nos expertises respectives pour déconstruire quelques idées reçues à ce sujet, et trouver un équilibre entre besoins humains et exigences algorithmiques. Étant donné la domination de Google en matière de SEO, nous avons centré cet article sur son moteur de recherche. Avant de commencer, vous devez savoir que l’accessibilité du web est définie et standardisée par plusieurs normes de référence : au niveau mondial : les Règles pour l’accessibilité des contenus web (WCAG, Web Content Accessibility Guidelines) et la spécification WAI-ARIA (en anglais) 2 du W3C (World Wide Web Consortium) ; dans l’Union européenne : la norme EN 301 549 (PDF en anglais) 3  ; en France : le Référentiel général d’amélioration de l’accessibilité (RGAA). Dans cet article, nous nous référons à la version 4.1.2 du RGAA, qui est la version en vigueur. Une démarche « accessibility first » (l’accessibilité d’abord) permettant d’optimiser votre référencement naturel, ça vous dit ? Alors, lisez ce qui suit. Si vous ne lisez pas tout, lisez au moins la conclusion, qui présente des considérations éthiques très importantes. Cas où l’on peut concilier l’accessibilité et le SEO 🤝 Images : référencer une image décorative Impact pour l’accessibilité Une image est décorative lorsqu’elle n’a aucune fonction et ne véhicule aucune information particulière par rapport au contenu auquel elle est associée, selon la définition du RGAA. Les images décoratives doivent être ignorées par les technologies d’assistance telles que les lecteurs d’écran dont se servent en particulier les personnes déficientes visuelles 4 . En effet, un lecteur d’écran leur permet d’accéder aux contenus et aux fonctionnalités d’un site ou d’une application web en restituant ou vocalisant l’information. Or, si toutes les images de décoration du web étaient restituées, cela produirait beaucoup d’informations inutiles et alourdirait la charge cognitive des personnes concernées. Le problème, c’est que des expert·es SEO souhaitent parfois optimiser toutes les images pour le référencement en remplissant leurs alternatives avec des mots-clés, même celles des images décoratives. Alors, que faire ? Solution Voici comment indexer une image décorative dans Google sans générer de bruit inutile pour les personnes aveugles ou malvoyantes : ajoutez l’attribut ARIA aria-hidden="true" sur chaque image décorative que vous tenez absolument à référencer 5 , puis renseignez l’alternative textuelle de l’image à l’aide des mots-clés de votre choix. C’est une méthode parfaitement conforme au RGAA 6  ; a contrario, ne remplissez pas l’alternative textuelle d’une image décorative si vous ne souhaitez pas qu’elle apparaisse dans les résultats de recherche d’images. Attention ! N’utilisez pas l’attribut aria-hidden="true" sur un composant interactif, tel qu’un lien, un bouton, un carrousel, etc. En effet, cela empêcherait les personnes qui naviguent à la voix d’interagir avec ce composant 7 , ce qui serait un grave problème d’accessibilité. N’utilisez pas le role="presentation" pour masquer les images décoratives à référencer. Certes, le test 1.2.1 du RGAA reconnaît comme décorative une image possédant à la fois une alternative textuelle renseignée et une propriété WAI-ARIA role="presentation". Mais les tests effectués par Access42 indiquent que, dans Firefox, tous les lecteurs d’écran préconisés par l’environnement de test du RGAA restituent l’image quand même. ☹ N’oubliez pas que les mots-clés utilisés dans l’alternative textuelle des images décoratives possédant la propriété aria-hidden="true" s’afficheront à l’écran si l’image ne se charge pas pour une raison ou pour une autre. Impact pour le SEO Google ne tient pas compte des attributs WAI-ARIA pour l’indexation et le référencement des pages web. Cette information a été confirmée par plusieurs experts SEO dans une discussion sur LinkedIn (en anglais), dont John Mueller et Martin Splitt de l’équipe Google Search Relations. De plus, l’attribut alt n’est pas un levier SEO magique : il doit avant tout servir l’accessibilité plutôt que d’être détourné pour le référencement, dixit Google lui-même 8 . Cela veut dire que bourrer l’attribut alt de mots-clés, même avec aria-hidden="true", est non seulement inutile, mais aussi potentiellement négatif pour votre référencement. Liens : intitulés des liens Parlons maintenant des liens aux intitulés a priori peu pertinents, comme « cliquer ici » ou « en savoir plus ». Impact pour l’accessibilité Ce type de liens ne pose pas de problème particulier pour l’accessibilité de votre site, tant que le contexte de chaque lien permet d’en comprendre la fonction et la destination. Chaque lien doit donc être explicite et posséder un intitulé 9 . Cela vous laisse une grande marge de manœuvre pour intituler vos liens comme bon vous semble. Certes, du point de vue de l’expérience utilisateur, une meilleure pratique consisterait à rendre chaque intitulé de lien explicite indépendamment de son contexte, comme le recommandent les WCAG 10 . Bien que cela soit facultatif, cette pratique contribue à améliorer l’expérience des personnes qui utilisent un lecteur d’écran, car elle leur permet de consulter la liste de tous les liens présents dans une page web et de comprendre leur fonction et leur destination, sans avoir à consulter le contexte de chacun d’eux. Impact pour le SEO Mais revenons à vos liens « cliquer ici » ou « en savoir plus » : ils sont en général déconseillés pour le SEO, car ils n’apportent aucune valeur sémantique aux moteurs de recherche et ne transmettent pas la valeur SEO d’une page à l’autre de manière efficace. Ce type de liens représente une opportunité manquée (en anglais) d’optimiser le maillage interne de votre site (en anglais). Pire : Google utilise parfois le texte d’ancrage des liens externes pour générer le « méta-titre » d’une page web – c’est-à-dire le titre présent entre les balises <title>, par opposition au titre de niveau 1 présent entre les balises

– lorsqu’il ne réussit pas accéder au contenu original de la page. Cela souligne l’importance d’avoir des intitulés de liens descriptifs et pertinents. Solution La meilleure approche pour gérer cette problématique SEO consiste à étendre le texte d’ancrage pour inclure des mots-clés pertinents et descriptifs. Par exemple, au lieu d’intituler simplement « Contact » un lien menant à votre formulaire de contact, il serait plus pertinent de l’intituler « Contacter notre agence SEO » ou bien « Découvrir notre audit technique ». Cette approche permet à la fois : d’améliorer la compréhension globale de la structure du site ; de créer des liens bien nommés qui plaisent aux moteurs de recherche. Attention ! Ayez la main légère quand vous placez des mots-clés dans les intitulés de liens : autrement, vous risquez de nuire à l’expérience utilisateur des personnes qui se servent d’un lecteur d’écran, en plus de déclencher une pénalité SEO. Structuration de l’information Titre d’une page web (balise ) Impact pour l’accessibilité Le titre d’une page web doit permettre d’identifier de manière claire, concise et unique les contenus ou la nature de la page. Chaque page doit donc posséder un titre pertinent 11 , c’est-à-dire un titre qui permet aux personnes utilisant des technologies d’assistance, en particulier des lecteurs d’écran, de savoir où elles se trouvent dès le chargement de la page, et de retrouver la page dans leur historique de navigation, ou dans la liste des onglets ouverts dans leur navigateur 12 . Il est très important pour l’accessibilité que le titre de votre page soit rédigé dans la même langue que les contenus de la page eux-mêmes 13  : cela permet aux lecteurs d’écran de lire l’information dans la langue appropriée. Impact pour le SEO Pour le référencement, la balise <title> n’est plus ce qu’elle était, puisque Google la réécrit dans de nombreux cas, sans vous demander votre avis. Ce « méta-titre » est un élément SEO si stratégique qu’il a mené à toutes sortes d’abus et de contenus trompeurs 14 . C’est pourquoi, à partir de 2012, Google a commencé à réécrire certains titres lui-même, en particulier quand il les jugeait trop longs. Capture d’écran 1 : Exemple de réécriture de méta-titre par Google. Sur un site web, le méta-titre est « Prévision des couleurs : d’où viennent les tendances de couleurs ? ». Capture d’écran 2 : Google a raccourci le titre visible sur la capture d’écran précédente en remplaçant la deuxième occurrence du mot « couleurs » par des points de suspension. Aujourd’hui, Google va encore plus loin : la balise <title> est désormais un élément dynamique qu’il peut entièrement réécrire s’il estime que votre titre initial n’est pas intéressant pour les utilisateurs et utilisatrices, ou s’il présente l’un des problèmes ci-dessous : titre sémantiquement pauvre : votre titre reflète mal le contenu de la page, et ne communique pas assez de valeur pour les personnes qui consulteront les résultats de Google ; incohérences linguistiques : un titre en anglais pour une page en français perturbe la compréhension de votre page par Google, en plus de beaucoup nuire à l’expérience utilisateur des personnes utilisant un lecteur d’écran ; titres inexacts : Google considère comme du clickbait (« piège à clics ») les titres qui ne reflètent pas le contenu réel de la page ; titres obsolètes : Google valorise l’exactitude temporelle. Par exemple, si vous avez publié un article intitulé « Quelles sont les couleurs tendance en 2023 ? », ce serait une mauvaise pratique de le modifier l’année suivante en écrivant « Quelles sont les couleurs tendance en 2024 ? », dans l’espoir de tromper Google sur la fraîcheur de votre article ; Capture d’écran 3 : un résultat dans Google indique que le site westwing.fr contient une page consacrée aux couleurs tendance, intitulée « Déco : quelles sont les couleurs tendance en 2023 ? » titres répétitifs : la répétition du même titre sur différentes pages est un obstacle pour le référencement. Google a besoin d’identifier et de différencier le caractère unique de chaque page web afin de présenter des résultats pertinents dans son moteur de recherche ; titre trop court : Google semble favoriser l’utilisation optimale de l’espace visuel au sein des résultats de recherche. Un titre trop court peut indiquer un titre insuffisamment pertinent. Si Google estime que votre « méta-titre » présente l’un de ces problèmes, il va le réécrire en utilisant plusieurs sources issues de votre page web : balise <title> ; titre de niveau 1 (balise h1) ; balise meta Open Graph og:title ; contenus proéminents ; ancres de texte sur les liens qui pointent vers votre page depuis d’autres sites web ; données structurées. Solution Pour écrire un « méta-titre » à la fois pertinent pour l’accessibilité et que Google ne va pas réécrire (enfin, on l’espère…) : choisissez un titre unique, descriptif et concis pour chaque page, sans céder à la tentation du « keyword stuffing » 15  ; rédigez le titre dans la même langue que le contenu de la page ; si la page est paginée 16  : incluez le numéro de la page dans son titre. Néanmoins, gardez en tête que Google dépriorise les pages paginées dans son index, car il les considère comme du contenu de moindre intérêt (« thin »). Enfin, pour améliorer l’UX et le SEO, la marque ou le titre de votre site devraient être ajoutés dans le titre de chaque page, généralement après un séparateur. Titres dans le contenu de la page (balises h1, h2, h3, etc.) Impact pour l’accessibilité Le contenu et la hiérarchie des titres de niveau 1 à 6 doivent être pertinents pour permettre aux personnes utilisant des technologies d’assistance de comprendre la structure de vos pages, et d’y naviguer à l’aide des raccourcis clavier fournis par les lecteurs d’écran 17 . Avoir une hiérarchie stricte entre les différents niveaux de titres est une bonne pratique pour l’accessibilité. Cependant, il n’est pas interdit, dans une même page web : de sauter un niveau de titre (par exemple, un h4 qui suit un h2) ; d’avoir plusieurs h1 ; de n’avoir aucun h1 ; de n’avoir aucun titre du tout. Pour mieux connaître ces subtilités, lisez l’article de David Swallow : En-têtes et non-conformité aux WCAG : une clarification à juste titre. Impact pour le SEO Google accorde une attention particulière à la structure sémantique du contenu : les balises de titre sont donc stratégiques pour l’aider à comprendre l’organisation et la pertinence de votre contenu, et ainsi maximiser son impact SEO. Vous devez donc soigner la rédaction des titres de vos contenus web. Les titres doivent être naturels et descriptifs, en intégrant les mots-clés pertinents pour votre stratégie, tout en évitant le piège du keyword stuffing, déjà évoqué. Voici la question principale que se posent les expert·es SEO : « Est-il possible de comprendre de quel sujet traite la page en lisant uniquement la hiérarchie des titres qui y sont présents ? ». Dans l’exemple suivant, il sera compliqué pour Google de comprendre qu’il s’agit de la page d’accueil de la station de ski Mont-Tremblant située au Québec, en consultant uniquement les titres de la page. Hiérarchie de titres HTML imparfaite. Le titre principal est « Réserver maintenant » en h1, suivi de sous-sections comme « Hébergement », « Ski », « Leçons », « Activités » et « Location » en h2. Les titres suivants, intitulés « Vacances d’hiver », « Billets de ski 2024/25 » et « Escapade d’automne », sont structurés avec une balise h6 : cela perturbe la structure, car il manque les niveaux intermédiaires h3, h4, et h5. Enfin, un dernier titre h3 est utilisé pour la section « Ouverture de la saison de ski ». La hiérarchie des titres est aussi importante du point de vue SEO, car Google ne prête pas la même attention aux titres au-delà du niveau 3. Pour plus d’informations, consultez les recommandations officielles de Google pour rédiger des titres (en anglais). Enfin, pour optimiser la hiérarchie de contenu d’une page, demandez-vous pour chaque titre : « Ce titre permet-il de mieux comprendre le thème du contenu principal de la page ? ». Solution Privilégiez les balises HTML natives pour titrer vos contenus : h1, h2, h3, h4, h5, h6. Si votre expert·e SEO vous déconseille d’utiliser une balise native dans des cas très précis : utilisez, ponctuellement, un titre WAI-ARIA avec role="heading" (en anglais) et la propriété aria-level adaptée. Prenons un exemple. Si le pied de page de votre site contient en permanence un bloc permettant de s’abonner à votre newsletter, il ne serait pas pertinent, côté SEO, d’utiliser une balise HTML h2 ou h3 pour titrer ce bloc. En effet, comme ce titre est commun à plusieurs pages, il ne renforcera pas le caractère unique et original de chaque page par rapport aux autres pour Google. Toutefois, pour l’accessibilité, il est essentiel que ce bloc dispose d’un titre : c’est ce qui va permettre aux personnes handicapées utilisant une technologie d’assistance de se repérer dans la page et d’y naviguer plus facilement. C’est pourquoi, dans ce cas précis, avoir recours à un titre WAI-ARIA offre un compromis intéressant pour prioriser l’accessibilité tout en défendant vos intérêts SEO. Ainsi, sur le site d’Access42, nous avons décidé de titrer le bloc « Recevez votre veille sur l’accessibilité numérique par e-mail » avec un titre ARIA de niveau 2. Il n’y a pas de balise HTML h2 : en effet, écrire <p role="heading" aria-level="2">Recevez votre veille sur l’accessibilité numérique par e-mail</p> équivaut, pour les lecteurs d’écran, à écrire <h2>Recevez votre veille sur l’accessibilité numérique par e-mail</h2>, mais sans l’éventuel impact négatif dans Google lié à la redondance du bloc d’une page à l’autre. Attention ! ARIA peut nuire à l’accessibilité si vous l’utilisez mal. Voici des erreurs courantes à éviter absolument : de manière générale, n’utilisez pas les propriétés role="heading" et aria-level sur les balises h1, h2, h3, h4, h5, h6, car il est important de respecter la sémantique des éléments HTML ; n’utilisez pas la propriété role="presentation" sur les balises h1, h2, h3, h4, h5, h6 pour la même raison ; n’utilisez pas aria-hidden="true" sur des textes masqués visuellement avec une classe CSS comme .sr-only, dans l’espoir d’éviter leur indexation par Google. Tout ce que vous allez faire, c’est rendre ces titres inaccessibles pour les personnes handicapées, sans empêcher leur référencement ; n’utilisez pas aria-hidden="true" sur les titres h1, h2, h3, h4, h5, h6 pour empêcher qu’ils ne soient pris en compte par Google : rebelote, celui-ci n’en tiendra pas compte, mais vous empêcherez les personnes handicapées de naviguer dans votre page. Cas où l’accessibilité et le SEO s’opposent 💣 – et où l’accessibilité doit primer ! Citations : ne pas confondre la balise <cite> (accessibilité) et l’attribut cite (SEO) Impact pour l’accessibilité Vous l’avez compris : une des clés de l’accessibilité web, c’est le respect de la sémantique HTML. Tout comme les paragraphes, les listes ou les titres, les citations nécessitent elles aussi d’être correctement structurées 18 . Une distinction doit être faite entre les citations courtes (balise q), et les blocs de citation (balise blockquote). La balise cite (en anglais), quant à elle, peut être utilisée pour citer le titre d’une œuvre dont est tirée une citation, comme dans l’exemple suivant : <p>La citation suivante est un extrait du livre <cite>Survivre au taf. Stratégies d’autodéfense pour personnes minorisées</cite> de Marie Dasylva :</p> <blockquote> <p>Nous avons donc travaillé sur la notion d’excellence, et je le répète : l’excellence, la vraie, est celle qui ne vous tue pas. (…) Déconstruire cette notion commence par le fait d’accepter de devoir s’organiser autour de son handicap au lieu de vouloir montrer qu’on l’a dépassé à tout prix.</p> </blockquote> Ne confondez pas la balise <cite> avec l’attribut cite, qui contient une URI ou une URL menant à la source de la citation. Cet attribut est utilisé uniquement à des fins de SEO, étant donné qu’il est inaccessible aux êtres humains dans la plupart des cas, sauf lorsqu’on utilise le lecteur d’écran JAWS a priori 19 . Impact pour le SEO Les directives de qualité E-E-A-T (Expérience, Expertise, Autorité, Fiabilité) et la mise à jour HCU 20 de Google soulignent l’importance des citations dans le référencement web, bien qu’elles ne soient pas des facteurs de classement directs. Les citations renforcent la crédibilité de votre contenu en l’enrichissant et en soulignant son niveau d’expertise. C’est un outil stratégique qui démontre la fiabilité de votre site et qui, par extension, soutient votre autorité de marque. Cependant, si Google n’accorde aucun traitement spécial au contenu placé dans une balise blockquote, une citation trop longue peut être perçue comme du contenu dupliqué. Cela veut dire que le moteur de recherche peut choisir de ne pas indexer certaines pages considérées comme dupliquées à cause d’un contenu trop similaire à celui d’une autre page. Par ailleurs, l’attribut cite a plusieurs fonctions techniques, mais son impact SEO est limité. S’il peut être lu par les moteurs de recherche, en revanche il ne protège pas contre le contenu dupliqué. Solution Distinguez les citations courtes des blocs de citations grâce aux balises HTML appropriées : q et blockquote. Si besoin, citez l’œuvre dont est extraite chaque citation à l’aide de la balise <cite>. Pour optimiser l’impact SEO d’une citation, utilisez l’attribut cite sur la balise blockquote pour indiquer l’URL dont elle est issue. Privilégiez les sources académiques et les études originales, et veillez à limiter le contenu cité par rapport à votre propre contenu, pour éviter qu’il ne soit considéré comme du contenu dupliqué par Google. Pour en savoir plus, consultez des exemples dans l’article de Rodolphe sur le blog d’Alsacréations : Les citations en HTML avec blockquote, cite et q. Cas qui peuvent créer des obstacles pour l’accessibilité et le SEO 🙈 Liens Liens composites Un lien composite est un bloc entièrement cliquable (balise HTML a) qui regroupe à la fois du texte ainsi qu’un ou plusieurs éléments de type image (balises img, area, object, canvas ou svg). Impact pour l’accessibilité Un lien composite ne pose pas, en soi, de problème pour l’accessibilité, si son intitulé est pertinent, c’est-à-dire s’il permet de comprendre la fonction et la destination du lien 21 . C’est particulièrement important pour les personnes qui présentent une déficience visuelle et qui n’ont pas une vision globale de la page : un intitulé de lien pertinent leur permet de comprendre la fonction de chaque lien, qu’il contienne une image, du texte, ou les deux à la fois. Créer des liens composites est même une bonne pratique pour supprimer la redondance créée par deux liens adjacents menant à la même URL (par exemple un lien image puis un lien texte). En effet, selon les personnes, il peut être plus ou moins facile d’identifier un lien grâce à une icône, ou bien grâce à son intitulé : réunir les deux au sein d’un seul et unique lien permet donc d’améliorer son accessibilité 22 . Maintenant, un lien composite qui contiendrait beaucoup de texte peut nuire à l’expérience utilisateur, notamment pour les personnes utilisant un lecteur d’écran, la mémoire tampon de ces outils étant limitée, la restitution de l’intitulé pourrait être hachée, ce qui peut rendre complexe sa compréhension. Impact pour le SEO Au niveau SEO, les blocs entièrement cliquables empêchent les robots de Google de comprendre l’ancre explicitement sans le contexte des autres éléments du bloc. Il vaut mieux conserver des liens distincts, et étendre la zone de clic si besoin avec CSS et/ou JavaScript. En effet, la longueur du texte d’ancrage n’est pas un critère déterminant pour le SEO, car Google privilégie les intitulés de lien pertinents et naturels qui décrivent la destination du lien. Dans l’article Internal Linking Anchor Texts - Text Variety is Key According to SEO Studies, Daniel Højris Bæk démontre que les sites privilégiant la diversité des textes d’ancrage surpassent leurs concurrents avec un classement SEO moyen de 1,3 (contre 3,5) et un engagement utilisateur supérieur de 50 %, la longueur optimale d’un intitulé de lien se situant autour de 5 mots ou 24 caractères. Pour en savoir plus : Anchor Text : Short vs. Long – What Is Best for SEO? Anchor Text : Types, SEO Implications, and Best Practices The Importance of Anchor Text Diversity Solution Faites attention à la longueur de l’intitulé des liens composites. Si besoin, étendez la zone de clic du lien tout en vous assurant que chaque lien (balise a) reste accessible au clavier. Dans l’article Enhancing The Clickable Area Size, Robin Rendle partage des ressources proposant plusieurs manières de faire. Obfuscation des liens L’obfuscation des liens est une pratique très controversée (pour ne pas dire : d’un autre âge) qui consiste à masquer ou modifier la présentation des liens pour influencer les moteurs de recherche. Cela pose de sérieux problèmes, à la fois pour l’accessibilité et le SEO. Impact pour l’accessibilité Un lien obfusqué pose un problème majeur pour les personnes qui naviguent au clavier, dont des personnes ayant un handicap moteur et/ou un handicap visuel. Voici un exemple de lien obfusqué : <span data-lien="/mentions-legales">Mentions légales</span>. Visuellement, cela peut ressembler à un lien véritable. Techniquement, un script va détecter la cible de ce faux lien, et ouvrir l’URL correspondante lorsque l’utilisatrice cliquera dessus avec sa souris ou son trackpad. Le problème, c’est que le focus ne peut pas être déplacé sur ce faux lien. Non contents de ruiner l’accessibilité de votre page, les faux liens posent d’autres problèmes d’utilisabilité, qui impactent un très large public, comme l’explique très bien Julie Moynat dans l’article Obfuscation de liens en SEO et problèmes d’accessibilité. Impact pour le SEO Google a pris fermement position contre l’obfuscation des liens, qu’il considère comme du « black hat SEO », c’est-à-dire comme une mauvaise pratique qui va à l’encontre de ses recommandations. Selon lui, obfusquer un lien est une tentative de manipulation du référencement. Si vous utilisez ce type de méthode, votre site risque donc d’être lourdement pénalisé dans les résultats de recherche de Google. L’obfuscation des liens est souvent utilisée pour masquer les liens qui mènent à des contenus affiliés. Or, selon John Mueller, représentant de Google (lien en anglais), les liens d’affiliation sont parfaitement acceptables : il est inutile de les masquer ou de les obfusquer sous prétexte de vouloir conserver un bon référencement. Solution N’obfusquez pas les liens, même pas les liens affiliés : n’utilisez pas JavaScript pour modifier la cible des liens sur les actions « onclick » ni d’autres méthodes de type « cloaking » (c’est-à-dire servir un contenu à Google tout en en servant un autre aux êtres humains). Utilisez l’attribut rel="sponsored" sur chaque lien affilié. Cela permet de signaler clairement la nature commerciale du lien aux moteurs de recherche. Cette transparence vous évitera des impacts négatifs sur votre référencement. Si vous n’avez vraiment pas le choix, optez pour une méthode d’obfuscation accessible : elle doit notamment permettre d’atteindre chaque faux lien au clavier, à la voix, ou au moyen d’un lecteur d’écran. Pour en savoir plus, consultez l’idée pour obfusquer les liens de façon accessible proposée par Julie Moynat et Romain Gervois. Attention ! L’obfuscation de liens, même avec une approche plus accessible, est une pratique non seulement dépassée, mais surtout risquée du point de vue du SEO : en effet, Google peut l’interpréter comme une tentative de manipulation qui va à l’encontre de ses directives (en anglais). De plus, si vous utilisez JavaScript pour transformer les faux liens en vrais liens, cela peut ralentir le chargement de la page. Or, Google tient aussi compte de la vitesse de chargement dans son algorithme (en anglais). CQFD. Question subsidiaire : Google tient-il compte de l’accessibilité pour le référencement ? Sundar Pichai, président-directeur général de Google depuis 2015, affirme que l’accessibilité est une valeur fondamentale de l’entreprise, inscrite dans leur mission et appliquée à leurs propres produits (en anglais). Google cherche à prouver cet engagement en partageant, pour l’ensemble de leurs services, les rapports sur leur conformité à l’accessibilité. Cependant, John Mueller, spécialiste des questions de référencement et porte-parole de Google, a adopté une position plus nuancée concernant les sites web tiers dans une interview de 2022 (en anglais) : selon lui, l’accessibilité n’est pas un facteur direct de classement et Google n’a pas pour projet d’en faire un éventuel critère de pertinence. La raison principale tient, selon lui, à l’impossibilité technique de quantifier objectivement l’accessibilité d’un site web. Il n’exclut pas que cela puisse changer à l’avenir en évoquant un système de mesure de l’accessibilité similaire aux Core Web Vitals de Google, un outil permettant de mesurer la qualité d’une page web. Cette possibilité serait d’autant plus pertinente avec l’entrée en vigueur de nouvelles réglementations à travers le monde. Toutefois, à ce jour, Google ne semble pas avoir lancé de véritable projet en ce sens. Capture d’écran 4 : Google does not give a damn about accessibility. But we as humans should. Extrait de la conférence How to ensure that your technical SEO strategy is truly accessible que Billie Geena a donnée à Brighton SEO en 2023. Conclusion L’accessibilité et le SEO ne sont pas fondamentalement incompatibles, mais leur relation reste souvent déséquilibrée. Si de nombreuses bonnes pratiques peuvent servir les deux objectifs, il est essentiel de garder à l’esprit que l’accessibilité doit toujours primer sur le SEO. Cette hiérarchisation est cruciale. En effet, le SEO repose sur des algorithmes dont le principal objectif est la génération de profit, tandis que l’accessibilité vise à répondre aux besoins et droits fondamentaux des personnes en situation de handicap, en leur permettant d’accéder au web et aux outils numériques sans obstacle. C’est pourquoi, affirmer que l’accessibilité améliorerait le SEO est risqué : lorsque les deux entrent en conflit, c’est souvent le SEO qui l’emporte, au détriment des besoins humains. La solution réside dans une approche « accessibility first », similaire au concept « mobile first » (le mobile d’abord). En concevant d’abord pour l’accessibilité, vous créez des interfaces qui répondent aux besoins des utilisateurs et des utilisatrices, tout en anticipant l’évolution des moteurs de recherche vers plus d’inclusivité. Car si Google affirme aujourd’hui que l’accessibilité n’est pas un facteur de classement direct, les nouvelles réglementations et l’évolution des usages le forceront tôt ou tard à intégrer ces principes dans ses outils pour rester pertinent. C’est donc à vous, professionnel·les du numérique, de montrer l’exemple en plaçant l’accessibilité au cœur de tous vos projets numériques. Cela implique de prendre en compte les besoins des personnes handicapées dès la phase de conception, puis d’adapter votre stratégie de référencement en conséquence. La réussite d’une démarche « accessibility first » dépend aussi beaucoup des bons choix de prestataires. Que ce soit pour le design UX/UI, le développement, la production éditoriale, la gestion de projet, l’accessibilité ou le SEO, il est essentiel de travailler avec des expert·es qui comprennent les enjeux de l’accessibilité. En particulier, les expert·es SEO ayant cette sensibilité sauront ajuster leurs recommandations pour allier référencement et accessibilité, contribuant ainsi à la réussite de vos projets web. Remerciements Nous adressons nos plus sincères remerciements aux personnes ayant permis d’enrichir cet article et de vérifier son accessibilité : Audrey Maniez, Luce Carević, Maïa Kopff et Philippe Bouchon d’Access42, ainsi que l’équipe de Paris Web. Search Engine Optimization ↩ Retour au texte 1 ARIA permet de rendre accessibles des composants complexes, en ajoutant de la sémantique et des métadonnées aux contenus HTML (Hypertext Markup Language) pour les lecteurs d’écran. ↩ Retour au texte 2 La norme européenne est également utilisée ailleurs dans le monde, par exemple au Canada, qui en fournit une version HTML. ↩ Retour au texte 3 cf. critère 1.2 du RGAA ↩ Retour au texte 4 Malheureusement, l’option n’est pas disponible dans les éditeurs WYSIWYG de manière native. Si besoin, rapprochez-vous de l’équipe qui développe votre site ou votre thème pour leur faire part de cette demande. ↩ Retour au texte 5 cf. test 1.2.1 du RGAA ↩ Retour au texte 6 cf. cette note en anglais ↩ Retour au texte 7 cf. cette vidéo en anglais : Je me concentrerais davantage sur l’aspect de l’accessibilité que sur l’aspect purement SEO. selon John Mueller, Search Liaison chez Google. Cependant, les bonnes pratiques officielles de Google ne traitent pas des images décoratives : cela peut laisser penser qu’il serait essentiel de renseigner l’alternative de toutes les images, alors que c’est une mauvaise pratique pour l’accessibilité. ↩ Retour au texte 8 cf. critère 6.1 et critère 6.2 du RGAA ↩ Retour au texte 9 Il s’agit du critère de succès 2.4.9 Fonction du lien (lien uniquement) dans les WCAG 2.1, qui est de niveau triple A (AAA). Bien que ce critère faisait l’objet du critère 6.3 dans le RGAA 3, il a été supprimé du RGAA 4. Il est simplement cité dans une de ses annexes. ↩ Retour au texte 10 cf. critère 8.5 et critère 8.6 du RGAA ↩ Retour au texte 11 cf. test 8.6.1 du RGAA ↩ Retour au texte 12 cf. critères 8.3 et critère 8.4 du RGAA ↩ Retour au texte 13 Par exemple : insérer de fausses dates dans le titre pour mieux ressortir dans les résultats de Google ; ou encore décrire des contenus de manière trompeuse dans le titre d’une page, par exemple en incluant une marque populaire de chaussures dans le titre d’une page pour faire référencer une page vendant des chaussures, même si celles-ci ne sont pas de la marque indiquée. ↩ Retour au texte 14 Le « keyword stuffing » consiste à ajouter des mots clés de manière excessive et artificielle dans l’espoir d’améliorer le référencement d’une page. ↩ Retour au texte 15 Par exemple, les nombreuses pages d’un même résultat de recherche. Un ensemble de pages paginées s’appelle une « collection de pages » dans le vocabulaire du RGAA. Cette notion se trouve encore dans le RGAA 4. ↩ Retour au texte 16 cf. critère 9.1 du RGAA ↩ Retour au texte 17 cf. critère 9.4 du RGAA ↩ Retour au texte 18 Seul JAWS restitue la présence de cet attribut dans certains contextes. Pour en savoir plus : Blockquotes in Screen Readers d’Adrian Roselli et HTML5 Accessibility Chops: section elements de Steve Faulkner (cf. le tableau sur JAWS). ↩ Retour au texte 19 E-E-A-T évalue la qualité du contenu, tandis que le HCU favorise le contenu utile pour les utilisateurs et utilisatrices. ↩ Retour au texte 20 cf. test 6.1.3 du RGAA ↩ Retour au texte 21 cf. Technique WCAG H2: Combining adjacent image and text links for the same resource ↩ Retour au texte 22</div> <div style="height:20px;"> </div> <div class="timeline-thumbnail" title="Accessibilité versus SEO : le duel qui façonne le web" > <div class="timeline-thumbnail-content" style="background-image: url(https://feedbot.net/storage/thumbnails/2024_12/ac5b1d08f9771e2f557a8412fde05333-639a385ec0a0e0b441c3c8268c905658.jpg);"> <a href="https://www.24joursdeweb.fr/2024/accessibilite-versus-seo-le-duel-qui-faconne-le-web" target="_blank" style="display:block; width:100%; height:100%;"></a> </div> <div class="timeline-title"> <a href="https://www.24joursdeweb.fr/2024/accessibilite-versus-seo-le-duel-qui-faconne-le-web" target="_blank" style="text-decoration: none;"><span style="color:var(--feedbot-gray); font-size: 12px; text-transform: uppercase;">www.24joursdeweb.fr</span><br /><span style="color: var(--feedbot-title); font-weight: bold; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;">Accessibilité versus SEO : le duel qui façonne le web</span></a> </div> </div> <div style="position:relative; display: block; height: 28px;"> <div class="timeline-buttons-left"> <form class="timeline-buttons share_765219"> <input type="hidden" name="article_id" value="765219"> <input type="hidden" name="feed_id" value="364"> <input type="hidden" name="site_id" value="160"> <input type="hidden" name="messagetitle" value="Accessibilité versus SEO : le duel qui façonne le web"> <input type="hidden" name="message" value="« Google est souvent qualifié de “ premier utilisateur aveugle du Web ”. Cette idée mérite d’être déconstruite, car contrairement aux personnes en situation de handicap qui cherchent simplement à consulter et à publier des contenus numériques, les robots de Google œuvrent uniquement pour la performance commerciale.Comme chien et chat ? Photo de Alexander Grey pour Pexels.Malgré tout, concilier accessibilité et référencement… »"> <input type="hidden" name="url" value="https://www.24joursdeweb.fr/2024/accessibilite-versus-seo-le-duel-qui-faconne-le-web"> <input type="hidden" name="peertubeid" value=""> <input type="hidden" name="youtubeid" value=""> <input type="hidden" name="thumbnail" value="https://feedbot.net/storage/thumbnails/2024_12/ac5b1d08f9771e2f557a8412fde05333-639a385ec0a0e0b441c3c8268c905658.jpg"> <button onclick="share(765219)" class="timeline-buttons" title="SHARE"> <i class="fa fa-retweet" aria-hidden="true" style="margin-right: 5px;"></i> SHARE</span> </button> </form> <form class="timeline-buttons unshare_765219" style="display: none;"> <input type="hidden" name="action" value="delete_status"> <input type="hidden" name="article_id" value="765219"> <input type="hidden" name="status_id" value=""> <button onclick="unshare(765219)" type="submit" class="timeline-buttons" title="SHARED : THERE_IS1WEEKSAGO" style="color:var(--feedbot-purple);"> <i class="fa fa-retweet" aria-hidden="true" style="margin-right: 5px;"></i> SHARED</span> </button> </form> </div> <div class="timeline-buttons-right"> <form class="timeline-buttons bookmark_765219" class="timeline-buttons-div"> <input type="hidden" name="action" value="bookmark"> <input type="hidden" name="article_id" value="765219"> <input type="hidden" name="feed_id" value="364"> <input type="hidden" name="site_id" value="160"> <button onclick="bookmark(765219)" class="timeline-buttons" title="READ_LATER"> <i class="fa fa-bookmark" aria-hidden="true" style="margin-right: 5px;"></i> READ_LATER </button> </form> <form class="timeline-buttons unbookmark_765219" style="display:none;" class="timeline-buttons-div"> <input type="hidden" name="action" value="delete_bookmark"> <input type="hidden" name="article_id" value="765219"> <button onclick="unbookmark(765219)" class="timeline-buttons" title="READ_LATER" style="color:red;"> <i class="fa fa-bookmark" aria-hidden="true" style="margin-right: 5px;"></i> READ_LATER </button> </form> </div> </div></div> </div> <div class="widget-home"> <div class="widget-home-content small_screens_hide" style="margin-top: 20px; text-align: center;"> <h4 style="font-size:20px; margin-bottom: 20px; color:var(--feedbot-title);">ENJOY_USING Feedbot ?</h4> <p style="margin-bottom: 20px;">FUNDING</p> <script src="https://liberapay.com/raph/widgets/button.js"></script> <noscript><a href="https://liberapay.com/raph/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript> </div><div class="footer small_screens_hide">COPYRIGHT <a href="https://tooter.social/@raph" target="_blank" rel="me">Raph <i class="fa fa-mastodon" aria-hidden="true"></i></a><br /> Logo by <a href="https://maous.fr/" target="_blank">Maous <img src="https://feedbot.net/assets/maous.png" style="width:22px; margin-bottom:6px; vertical-align: middle;"/></a></div> </div> </div> <script> var isActive = false; var page = "single-feed"; var feed_id = "364"; var search = ""; $(window).scroll(function(){ get_sticky_position($(".widget-home")); }); $(window).on("resize", function(){ get_sticky_position($(".widget-home")); }); function linesCount($elem){ var article_id = $($elem).attr("data-article-id"); var lineHeight = parseFloat($($elem).css('line-height')); var elementHeight = $($elem)[0].scrollHeight; var lineCount = Math.floor(elementHeight / lineHeight); var read_more_elem = $(".read-more[data-article-id=" + article_id + "]").length; if(lineCount > 5){ $($elem).addClass("description-short"); if(read_more_elem == 0){ $($elem).addClass("description-short"); $(`<div class="read-more" data-article-id="` + article_id + `" style="width:100%; font-weight: bold; text-align: center; margin-top: 10px; cursor: pointer;">READ_MORE</div>`).insertAfter($elem); } } else{ $($elem).removeClass("description-short"); $(".read-more[data-article-id=" + article_id + "]").remove(); } } $(document).on("click", ".read-more", function(){ var article_id = $(this).attr("data-article-id"); $(".description[data-article-id=" + article_id + "]").removeClass("description-short"); $(".description[data-article-id=" + article_id + "]").attr("data-expanded", "true"); $(this).remove(); }); get_sticky_position($(".widget-home")); </script> </section> <script type="text/javascript"> // Mobile Safari in standalone mode if(("standalone" in window.navigator) && window.navigator.standalone){ // If you want to prevent remote links in standalone web apps opening Mobile Safari, change 'remotes' to true var noddy, remotes = false; document.addEventListener('click', function(event) { noddy = event.target; // Bubble up until we hit link or top HTML element. Warning: BODY element is not compulsory so better to stop on HTML while(noddy.nodeName !== "A" && noddy.nodeName !== "HTML"){ noddy = noddy.parentNode; } if('href' in noddy && noddy.href.indexOf('http') !== -1 && (noddy.href.indexOf(document.location.host) !== -1 || remotes)){ event.preventDefault(); document.location.href = noddy.href; } },false); } </script> <script type="text/javascript"> // Get the button: let mybutton = document.getElementById("myBtn"); // When the user scrolls down 20px from the top of the document, show the button window.onscroll = function() {scrollFunction()}; function scrollFunction() { if(document.body.scrollTop > 5000 || document.documentElement.scrollTop > 5000){ mybutton.style.display = "block"; } else{ mybutton.style.display = "none"; } } // When the user clicks on the button, scroll to the top of the document function topFunction(){ $('html, body').animate({ scrollTop: 0 }, 'medium'); } </script> </body> </html>