"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > 스마트 편집기 구축: 자동으로 URL을 감지하여 하이퍼링크로 변환

스마트 편집기 구축: 자동으로 URL을 감지하여 하이퍼링크로 변환

2024-11-04에 게시됨
검색:292

이것은 사용자 경험을 개선하기 위해 직장에서 생각해낸 아이디어입니다. 여기에는 URL을 자동으로 감지하고 사용자 유형에 따라 하이퍼링크로 변환하는 텍스트 상자를 구현하는 작업이 포함됩니다(소스 코드 Github/AutolinkEditor). 이 멋진 기능은 구현하기가 다소 까다로우며 다음 문제를 해결해야 합니다.

  • 텍스트 내 URL을 정확하게 감지
  • URL 문자열을 하이퍼링크로 변환한 후 커서 위치 유지
  • 사용자가 하이퍼링크 텍스트를 편집할 때 그에 따라 대상 URL을 업데이트합니다.
  • 텍스트에서 줄바꿈 유지
  • 텍스트 상자의 형식과 일치하는 텍스트 스타일을 사용하여 텍스트와 줄 바꿈을 모두 유지하면서 서식 있는 텍스트 붙여넣기를 지원합니다.

Building a Smart Editor: Automatically Detect URLs and Convert Them to Hyperlinks

...
 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 식별 및 추출

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와 같은 일반적인 이벤트, 커서 위치를 복원하기 위해 선택 및 범위를 사용하는 방법, 편집기의 기능을 달성하기 위해 요소의 노드를 처리하는 방법 등 간단한 편집기를 구현하는 데 사용되는 몇 가지 실용적인 기술을 설명합니다. 기능. 정규식은 이 기사의 초점이 아니지만 완전한 정규식은 특정 문자열을 식별하는 데 있어 편집기의 견고성을 향상시킬 수 있습니다(이 기사에서 사용된 정규식은 수정을 위해 계속 열려 있습니다). 프로젝트에 도움이 되는 경우 Github/AutolilnkEditor를 통해 소스 코드에 액세스하여 자세한 내용을 얻을 수 있습니다.

릴리스 선언문 이 기사는 https://dev.to/oninebx/building-a-smart-editor-automatically-Detect-urls-and-convert-them-to-hyperlinks-ilg?1에서 복제됩니다. 침해가 있는 경우, 문의 Study_golang@163 .comdelete
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3