@MaximPatrushev

Как оптимизировать и повысить масштабируемость конкретного примера javascript кода?

В целом задача состоит в том, чтобы сделать вывод реактивного оглавления для содержимого WYSIWYG редактора.

В качестве JS-фреймворка я использую React, в качестве редактора - ckeditor. Задача состояла в том, чтобы сделать следующее - при заполнении редактора контентом, заголовки 1-3 уровня парсятся и реактивно выводятся в виде оглавления рядом с окном редактора. При этом пункты в этом оглавлении должны быть кликабельны - по клику должно происходить прокручивание окна редактора до соответствующего заголовка. Имеющиеся плагины позволяют только выводить таблицу с оглавлением, но не делать ее элементы кликабельными.

Задачу я решил, но мне кажется, мой код весьма топорен и плохо масштабируем.
Ссылка на песочницу с примером

Допустим, если условия задачи поменяются и надо будет парсить заголовки 1-6 уровня, то мой код вырастет в 2 раза. Я пытался найти более изящное решение с применением рекурсий в этих моментах:

while (currentEl !== null) {
      if (currentEl.tagName === 'H1') {
        h1Array.push({
          text: currentEl.innerHTML,
          h2Array: []
        });
        i++;
        j = 0;
      } else if (currentEl.tagName === 'H2') {
        h1Array[i-1].h2Array.push({
          text: currentEl.innerHTML,
          h3Array: []
        });
        j++;
      } else if (currentEl.tagName === 'H3') {
        h1Array[i-1].h2Array[j-1].h3Array.push({
          text: currentEl.innerHTML
        });
      }

      currentEl = currentEl.nextElementSibling;
    }

и

// finding closest h2 tag
    let h2El = h1El.nextElementSibling;
    let i = 0;
    while (i <= +h2Index) {
      if (h2El.tagName === 'H2') {
        if (i === +h2Index) {
          break
        } else i++;
      }
      h2El = h2El.nextElementSibling;
    }

    // finding h3 tag
    let h3El = h2El.nextElementSibling;
    let j = 0;
    while (j <= +h3Index) {
      if (h3El.tagName === 'H3') {
        if (j === +h3Index) {
          break
        } else j++;
      }
      h3El = h3El.nextElementSibling;
    }

но у меня ничего не получилось.
  • Вопрос задан
  • 96 просмотров
Решения вопроса 1
0xD34F
@0xD34F Куратор тега React
если условия задачи поменяются и...

Очевидно же - надо сделать отдельный компонент. "Условия" передавать через props, чтобы было как-то так:

<EditorWithTableOfContents tags={[ 'h1' ]} />
<EditorWithTableOfContents tags={[ 'h2', 'h3' ]} />

Сбор информации о заголовках - достаточно получить элементы и сохранить пары тэг-текст. При создании оглавления индексы элементам указываете общие, без разделения по тэгам. Когда надо выполнить прокрутку, берёте внутри редактора элемент с этим индексом:

const EditorWithTableOfContents = ({ tags }) => {
  const [headers, setHeaders] = useState([]);
  const editorEl = useRef(null);

  const onChange = e => {
    setHeaders(Array.from(
      e.editor.document.$.querySelectorAll(tags.join(', ')),
      n => [ n.tagName, n.innerText ]
    ));
  };

  const goToAnchor = e => {
    const index = e.target.dataset.index;
    const d = editorEl.current.editor.document.$;

    d.documentElement.scrollTo({
      top: d.querySelectorAll(tags.join(', '))[index].offsetTop,
      behavior: 'smooth',
    });
  };

  return (
    <div>
      <CKEditor
        data={initData}
        onInstanceReady={onChange}
        onChange={onChange}
        ref={editorEl}
      />
      <h2>Содержание</h2>
      {headers.map(([Tag, text], index) => (
        <Tag key={index} data-index={index} onClick={goToAnchor}>
          {text}
        </Tag>
      ))}
    </div>
  );
};

https://codesandbox.io/s/for-https-qna-habr-com-q-...
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы