Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #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, но мне нужно обрабатывать именно выделение, а не весь блок. Удачи всем, и мне тоже!)
Ответить с цитированием
  #2 (permalink)  
Старый 23.06.2020, 20:38
Аватар для Белый шум
Профессор
Отправить личное сообщение для Белый шум Посмотреть профиль Найти все сообщения от Белый шум
 
Регистрация: 19.01.2012
Сообщений: 505

Проверял только в хроме.

<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-click 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 tags?</div> 
</body>
<script>
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() {
  if( !window.getSelection ) { return; }
    let sel = window.getSelection();
    if( !sel.rangeCount ) { return; }
    let range = sel.getRangeAt(0).cloneRange();
      
    let node, start, end;
    if( range.startContainer.nodeName == "#text" ){
      	node = range.startContainer.parentNode;
        start = range.startOffset;
        end = range.endOffset;
    } else {
      	node = range.startContainer.childNodes[ range.startOffset ];
        start = 0;
        end = node.childNodes[0].length;
    }
    //console.log( node.closest("h1") );
    if( node.closest("h1") && node.closest("h1").closest("#editor") )
    {
        let childNodes = node.closest("h1").parentNode.childNodes;
        for( var i=0; i<childNodes.length; i++ ){
          if( childNodes[i] == node ) {--i; break;}
        }
        if( childNodes[i].nodeName == "#text" ){
          start += childNodes[i].length;
          end += childNodes[i].length;
          node = node.closest("h1");
          node.outerHTML = node.innerHTML; //удалить тег
          range.setStart(childNodes[i], start);
          range.setEnd(childNodes[i], end);
          sel.removeAllRanges();
          sel.addRange(range);
          //console.log(range);
        }
    } else {
      	if( !range.collapsed ){
          let newTag = document.createElement('h1');
          range.surroundContents(newTag);
          sel.removeAllRanges();
          sel.addRange(range);
        }
    }
}
</script>
</html>
Ответить с цитированием
  #3 (permalink)  
Старый 24.06.2020, 14:13
Аватар для Kiten
Интересующийся
Отправить личное сообщение для Kiten Посмотреть профиль Найти все сообщения от Kiten
 
Регистрация: 18.05.2018
Сообщений: 16

Я тоже сделал:
function headerFormatter(arg) {
  let newTag = document.createElement(arg);
  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(arg).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;
        }
      }
      if (
        range.commonAncestorContainer.parentNode.nodeName == 'H1' ||
        range.commonAncestorContainer.parentNode.nodeName == 'H2' ||
        range.commonAncestorContainer.parentNode.nodeName == 'H3'
      ) {
        if (range.commonAncestorContainer.parentNode.innerHTML == sel.toString()) {
          let parent = range.commonAncestorContainer.parentNode;
          parent.remove();
          range.deleteContents();
          range.insertNode(document.createTextNode(parent.innerHTML));
          sel.removeAllRanges();
          sel.addRange(range);
        }
        console.log(range.endOffset);
        console.log(sel.toString().length);
        if (range.endOffset == range.commonAncestorContainer.parentNode.innerHTML.length) {
          let parent = range.commonAncestorContainer.parentNode;
          let first = document.createElement("h2");
          let second = document.createTextNode(parent.innerHTML.slice(range.startOffset, range.endOffset));
          first.appendChild(document.createTextNode(parent.innerHTML.slice(0, range.startOffset)));
          newNode = document.createElement("p");
          newNode.appendChild(document.createTextNode("New Node Inserted Here"));
          parent.remove();         
          range.insertNode(second);
          let secondRange = sel.getRangeAt(0).cloneRange();
          sel.removeAllRanges();
          sel.addRange(secondRange);
          secondRange.insertNode(first);          
        }
      }
      else {
        range.surroundContents(newTag);
        sel.removeAllRanges();
        sel.addRange(range);
      }
    }
  }
}


Но возможно, ваш способ более лаконичный, и в духе современного JavaScript.
Спасибо вам большое!
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как вставить iptv в тег “video” из HTML5 ? nexlez (X)HTML/CSS 1 21.03.2017 09:41
Как обернуть каждый тег в несколько DIV содержащих по 1 атрибуту от этих самых тегов? Zaxap Общие вопросы Javascript 10 08.11.2016 11:56
Как Найти координаты вектора в таблице? xTODx jQuery 9 18.06.2015 00:57
Как найти конец плоского файла Don_001 Общие вопросы Javascript 1 07.07.2009 12:47
как найти нужный объект? `p r o x y jQuery 2 05.05.2009 01:12