Показать сообщение отдельно
  #1 (permalink)  
Старый 22.06.2020, 21:25
Аватар для Kiten
Интересующийся
Отправить личное сообщение для Kiten Посмотреть профиль Найти все сообщения от Kiten
 
Регистрация: 18.05.2018
Сообщений: 16

Как найти тег, соответствующий выделению?
Всем привет!
Буду благодарен, если мне помогут разъяснить одну ситуацию. Я уже задал вопрос на стаке, но что-то эти индусы меня не поняли, или не заметили..
В общем, такая проблема: разрабатываю функционал, который форматирует выделенный текст. Я приведу в адаптированном виде часть своего кода, которая оборачивает выделенный текст в тег "h1".
Функция активируется при клике по кнопке. При первом клике выделенный текст превращается в заголовок, а при повторном клике теги удаляются, и мы видим предыдущий текст.
Проблема в том, что если убрать выделение (например выделить что-то другое), и снова выделить заголовок, и нажать кнопку-триггер, то результат будет иной - теги создаются снова, и заголовок становится больше.
Я оперирую методами Selection и Range. Получается, что при первом вызове создается range, выделенный текст оборачивается в тег h1, и все это входит в selection. Но при повторном выделении того же самого текста теги h1 выпадают из selection, и срабатывает та часть функции, которая создает теги (вместо той, которая их удаляет).

Для наглядности приведу ссылку на JsFiddle:
https://jsfiddle.net/narzantaria/2ex6bnq3/2/

А также приведу код. Вот листинг html:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button id="h1-trigger">Turn selection</button>
  <button id="check">Check selection status</button>
  <div id="editor">This is the text where we can select some word by double-clicking, and press the "h1-trigger button". The function wraps the selection with h1 tags. When clicked again it will unwrap the selection and return to previous text. But if we unselect the wrapped word (h1 heading), select by double-clich again, and press the button, it does another result. The second button shows the selection status with console log. Why re-selection doesn't contain the created tahs?</div> 
</body>
</html>

И JavaScript:
document.getElementById('h1-trigger').addEventListener('click', function () {
  headerFormatter();
});

document.getElementById('check').addEventListener('click', function () {
  let sel = window.getSelection();
  let range = sel.getRangeAt(0).cloneRange();
  let rangeProxy = sel.getRangeAt(0).cloneContents();
  console.log(sel);
  console.log(range);
  console.log(rangeProxy);
});

function headerFormatter() {
  let newTag = document.createElement('h1');
  if (window.getSelection) {
    let sel = window.getSelection();
    if (sel.rangeCount) {
      let range = sel.getRangeAt(0).cloneRange();
      // create an object from range for querying tags
      let rangeProxy = sel.getRangeAt(0).cloneContents();
      if (rangeProxy.querySelector('h1') || rangeProxy.querySelector('h2') || rangeProxy.querySelector('h3')) {
        let tagContent = rangeProxy.querySelector('h1').innerHTML;
        // compare selection length with queried tag length
        if (range.startOffset == 1) {
          tagContent = tagContent.replace(/(<([^>]+)>)/ig, "");
          range.deleteContents();
          range.insertNode(document.createTextNode(tagContent));
          sel.removeAllRanges();
          sel.addRange(range);
          return;
        }
        else {
          let rangeToString = range.toString().replace(/(<([^>]+)>)/ig, "");
          range.deleteContents();
          range.insertNode(document.createTextNode(rangeToString));
          sel.removeAllRanges();
          sel.addRange(range);
          return;
        }
      } else {
        range.surroundContents(newTag);
        sel.removeAllRanges();
        sel.addRange(range);
      }
    }
  }
}

P.S. Я знаю про Document.execCommand() и formatBlock, но мне нужно обрабатывать именно выделение, а не весь блок. Удачи всем, и мне тоже!)
Ответить с цитированием