C'est une idée que j'ai eue au travail pour améliorer l'expérience utilisateur. Il s'agit d'implémenter une zone de texte qui détecte automatiquement les URL et les convertit en hyperliens au fur et à mesure que l'utilisateur tape (code source Github/AutolinkEditor). Cette fonctionnalité intéressante est quelque peu délicate à mettre en œuvre et les problèmes suivants doivent être résolus.
... if(target && target.contentEditable){ ... target.contentEditable = true; target.focus(); } ...
La conversion est pilotée par les événements « onkeyup » et « onpaste ». Pour réduire la fréquence des conversions, un mécanisme de retard est implémenté avec « setTimeout », où la logique de conversion n'est déclenchée qu'après que l'utilisateur arrête de taper pendant 1 seconde par défaut.
idle(func, delay = 1000) { ... const idleHandler = function(...args) { if(this[timer]){ clearTimeout(this[timer]); this[timer] = null; } this[timer] = setTimeout(() => { func(...args); this[timer] = null; }, delay); }; return idleHandler.bind(this); }
Je n'avais pas l'intention de passer du temps à créer l'expression régulière parfaite pour faire correspondre les URL, j'en ai donc trouvé une utilisable via un moteur de recherche. Si quelqu'un en a un meilleur, n'hésitez pas à me le faire savoir !
... const URLRegex = /^(https?:\/\/(([a-zA-Z0-9] -?) [a-zA-Z0-9] \.) (([a-zA-Z0-9] -?) [a-zA-Z0-9] ))(:\d )?(\/.*)?(\?.*)?(#.*)?$/; const URLInTextRegex = /(https?:\/\/(([a-zA-Z0-9] -?) [a-zA-Z0-9] \.) (([a-zA-Z0-9] -?) [a-zA-Z0-9] ))(:\d )?(\/.*)?(\?.*)?(#.*)?/; ... if(URLRegex.test(text)){ result = `${escapeHtml(text)}`; }else { // text contains url let textContent = text; let match; while ((match = URLInTextRegex.exec(textContent)) !== null) { const url = match[0]; const beforeUrl = textContent.slice(0, match.index); const afterUrl = textContent.slice(match.index url.length); result = escapeHtml(beforeUrl); result = `${escapeHtml(url)}`; textContent = afterUrl; } result = escapeHtml(textContent); // Append any remaining text }
Avec document.createRange et window.getSelectionfunctions, calculez la position du curseur dans le texte du nœud. Étant donné que la conversion des URL en hyperliens ajoute uniquement des balises sans modifier le contenu du texte, le curseur peut être restauré en fonction de la position précédemment enregistrée. Pour plus de détails, veuillez lire Impossible de restaurer la sélection après une modification HTML, même s'il s'agit du même HTML.
Mettre à jour ou supprimer lors de la modification du lien hypertexte
Parfois, nous créons des hyperliens où le texte et l’URL cible sont identiques (appelés ici « hyperliens simples »). Par exemple, le HTML suivant montre ce type de lien hypertexte.
http://www.example.com
Pour de tels liens, lorsque le texte du lien hypertexte est modifié, l'URL cible doit également être automatiquement mise à jour pour les maintenir synchronisées. Pour rendre la logique plus robuste, le lien sera reconverti en texte brut lorsque le texte du lien hypertexte n'est plus une URL valide.
handleAnchor: anchor => { ... const text = anchor.textContent; if(URLRegex.test(text)){ return nodeHandler.makePlainAnchor(anchor); }else { return anchor.textContent; } ... } ... makePlainAnchor: target => { ... const result = document.createElement("a"); result.href = target.href; result.textContent = target.textContent; return result; ... }
Pour implémenter cette fonctionnalité, je stocke les « hyperliens simples » dans un objet et je les mets à jour en temps réel pendant les événements onpaste, onkeyup et onfocus pour garantir que la logique ci-dessus ne gère que les hyperliens simples.
target.onpaste = initializer.idle(e => { ... inclusion = contentConvertor.indexAnchors(target); }, 0); const handleKeyup = initializer.idle(e => { ... inclusion = contentConvertor.indexAnchors(target); ... }, 1000); target.onkeyup = handleKeyup; target.onfocus = e => { inclusion = contentConvertor.indexAnchors(target); } ... indexAnchors(target) { const inclusion = {}; ... const anchorTags = target.querySelectorAll('a'); if(anchorTags) { const idPrefix = target.id === "" ? target.dataset.id : target.id; anchorTags.forEach((anchor, index) => { const anchorId = anchor.dataset.id ?? `${idPrefix}-anchor-${index}`; if(anchor.href.replace(/\/ $/, '').toLowerCase() === anchor.textContent.toLowerCase()) { if(!anchor.dataset.id){ anchor.setAttribute('data-id', anchorId); } inclusion[[anchorId]] = anchor.href; } }); } return Object.keys(inclusion).length === 0 ? null : inclusion; ... }
Lors de la manipulation de texte enrichi collé, l'éditeur stylisera automatiquement le texte avec les styles de texte de l'éditeur. Pour conserver la mise en forme, les balises
dans le texte enrichi et tous les hyperliens seront conservés. La gestion du texte saisi est plus complexe. Lorsque l'utilisateur appuie sur Entrée pour ajouter une nouvelle ligne, un élément div est ajouté à l'éditeur, que l'éditeur remplace par un
pour conserver le formatage.
node.childNodes.forEach(child => { if (child.nodeType === 1) { if(child.tagName === 'A') { // anchar element const key = child.id === "" ? child.dataset.id : child.id; if(inclusion && inclusion[key]){ const disposedAnchor = handleAnchor(child); if(disposedAnchor){ if(disposedAnchor instanceof HTMLAnchorElement) { disposedAnchor.href = disposedAnchor.textContent; } result = disposedAnchor.outerHTML ?? disposedAnchor; } }else { result = makePlainAnchor(child)?.outerHTML ?? ""; } }else { result = compensateBR(child) this.extractTextAndAnchor(child, inclusion, nodeHandler); } } }); ... const ElementsOfBR = new Set([ 'block', 'block flex', 'block flow', 'block flow-root', 'block grid', 'list-item', ]); compensateBR: target => { if(target && (target instanceof HTMLBRElement || ElementsOfBR.has(window.getComputedStyle(target).display))){ return "
"; } return ""; }
Cet article décrit quelques techniques pratiques utilisées pour implémenter un éditeur simple, telles que les événements courants tels que onkeyup et onpaste, comment utiliser Selection et Range pour restaurer la position du curseur et comment gérer les nœuds d'un élément pour atteindre l'objectif de l'éditeur. fonctionnalité. Bien que les expressions régulières ne soient pas au centre de cet article, une expression régulière complète peut améliorer la robustesse de l'éditeur dans l'identification de chaînes spécifiques (l'expression régulière utilisée dans cet article restera ouverte à la modification). Vous pouvez accéder au code source via Github/AutolilnkEditor pour obtenir plus de détails si cela est utile pour votre projet.
Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.
Copyright© 2022 湘ICP备2022001581号-3