Это идея, которая пришла мне в голову на работе, чтобы улучшить взаимодействие с пользователем. Он включает в себя реализацию текстового поля, которое автоматически обнаруживает URL-адреса и преобразует их в гиперссылки по мере ввода пользователем (исходный код Github/AutolinkEditor). Эту замечательную функцию довольно сложно реализовать, и необходимо решить следующие проблемы.
... if(target && target.contentEditable){ ... target.contentEditable = true; target.focus(); } ...
Преобразование управляется событиями onkeyup и onpaste. Чтобы уменьшить частоту преобразований, реализован механизм задержки с помощью «setTimeout», где логика преобразования срабатывает только после того, как пользователь перестает печатать в течение 1 секунды по умолчанию.
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); }
Я не собирался тратить время на создание идеального регулярного выражения для сопоставления URL-адресов, поэтому нашел подходящее для использования через поисковую систему. Если у кого-то есть вариант получше, дайте мне знать!
... 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 }
С помощью функций document.createRange и window.getSelection рассчитайте положение курсора в тексте узла. Поскольку преобразование URL-адресов в гиперссылки только добавляет теги без изменения текстового содержимого, курсор можно восстановить на основе ранее записанной позиции. Для получения более подробной информации прочтите статью «Невозможно восстановить выделение после изменения HTML, даже если это тот же HTML».
Обновить или удалить при редактировании гиперссылки
Иногда мы создаем гиперссылки, в которых текст и целевой URL совпадают (здесь они называются «простыми гиперссылками»). Например, следующий HTML-код показывает гиперссылку такого типа.
http://www.example.com
Для таких ссылок при изменении текста гиперссылки целевой URL-адрес также должен автоматически обновляться, чтобы обеспечить их синхронизацию. Чтобы сделать логику более надежной, ссылка будет преобразована обратно в обычный текст, когда текст гиперссылки перестанет быть действительным URL-адресом.
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; ... }
Чтобы реализовать эту функцию, я сохраняю «простые гиперссылки» в объекте и обновляю их в режиме реального времени во время событий onpaste, onkeyup и onfocus, чтобы гарантировать, что приведенная выше логика обрабатывает только простые гиперссылки.
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; ... }
При обработке вставленного форматированного текста редактор автоматически стилизует текст с помощью текстовых стилей редактора. Для сохранения форматирования теги
в форматированном тексте и все гиперссылки будут сохранены. Обработка входного текста более сложна. Когда пользователь нажимает Enter, чтобы добавить новую строку, в редактор добавляется элемент div, который редактор заменяет на
для сохранения форматирования.
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 ""; }
В этой статье описаны некоторые практические методы, используемые для реализации простого редактора, такие как общие события, такие как onkeyup и onpaste, как использовать Selection и Range для восстановления положения курсора и как обрабатывать узлы элемента для достижения редактора. функциональность. Хотя регулярные выражения не являются предметом этой статьи, полное регулярное выражение может повысить надежность редактора при идентификации конкретных строк (регулярное выражение, используемое в этой статье, останется открытым для изменения). Вы можете получить доступ к исходному коду через Github/AutolinkEditor, чтобы получить более подробную информацию, если это полезно для вашего проекта.
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3