Quill, как один из самых современных текстовых редакторов, набрал более 35 тысяч звезд на Github. Его многочисленные полезные функции делают его расширяемым, совместимым и простым в использовании. Еще одна важная характеристика Quill заключается в его настраиваемости. Сама функция позволяет разработчикам адаптировать Quill для своих нужд.

При использовании Quill в сообщениях блога очень часто оглавление (TOC) размещается в верхней части сообщения. Это упрощает доступность контента для читателя, а также улучшения поисковой оптимизации (SEO). TOC, если он используется правильно, может одновременно нацеливаться на несколько ключевых слов в одном сообщении в блоге. Следовательно, сообщения могут ранжироваться по нескольким ключевым словам в поисковой выдаче! При этом Quill в сопровождении хорошего оглавления может заинтересовать многих разработчиков.

TOC для сообщений в блогах

Хотя существует много отличных модулей Quill, кажется, что ему не хватает простого оглавления, такого простого, как начать использовать сам Quill. В этой статье мы попробуем создать простую оглавление для поста в блоге. Мы предполагаем, что этот пост в блоге имеет несколько тегов заголовков, и мы стремимся создать список в виде кликабельного оглавления и поместить его в начало поста. Для демонстрации вы можете посмотреть эту запись в блоге.

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

Ссылки без целевого атрибута

В Quill кажется, что target="_blank" для ссылок является поведением по умолчанию, поэтому, если у нас есть ссылки для элементов TOC, он открывает ссылку на новой странице, что не является ожидаемым поведением для элементов TOC. Это должна быть ссылка с target="_self". На самом деле должно быть два разных типа ссылок, один для оглавления, а другой для обычных ссылок. Ну, настраиваемость Quill может помочь здесь.

            var Link = Quill.import('formats/link');

            class MyLink extends Link {
                static create(value) {
                    let node = Link.create(value);
                    value = Link.sanitize(value);
                    node.setAttribute('href', value);
                    if (value.startsWith("#")) {
                        node.removeAttribute('target');
                    } else {
                        node.setAttribute("target", "_blank");
                    }
                    return node;
                }

                format(name, value) {
                    super.format(name, value);

                    if (name !== this.statics.blotName || !value) {
                        return;
                    }

                    if (value.startsWith("#")) {
                        this.domNode.removeAttribute("target");
                    } else {
                        this.domNode.setAttribute("target", "_blank");
                    }
                }
            }

            Quill.register(MyLink);

Он расширяет модуль Quill Link и в методе создания смотрите на значение href, если оно начинается с # , атрибут target будет удален, и будет применяться его поведение по умолчанию (_self). Для других ссылок открывается новая страница.

Теги заголовков с атрибутом id

Чтобы иметь заголовки с атрибутом id, можно изменить модуль заголовка следующим образом:

            var ids = [];

            function getRandomId(domNode) {
                let _id = Math.random().toString(16).slice(2, 9);
                ids.push(_id);
                return _id;
            }

            class MyHeader extends Header {

                constructor(domNode) {
                    let a = super(domNode);
                    domNode.setAttribute('id', getRandomId(domNode));
                    this.cache = {};
                }

                static create(value) {
                    const node = super.create();
                    return node;
                }

                static formats(domNode) {
                    return {
                        id: domNode.getAttribute("id")
                    };
                }
            }

            Quill.register("formats/header", MyHeader);
            MyHeader.blotName = "header"

Обратите внимание, что с помощью функции getRandomId() мы временно присваиваем уникальный идентификатор каждому заголовку. Массив ids хранит сгенерированные идентификаторы для последующего использования. Эти значения будут заменены соответствующими и значимыми идентификаторами. Теперь у нас есть строительные блоки нашей работы, и мы можем использовать их для создания оглавления для этого поста в блоге. Но мы еще не инициализировали наш редактор Quill, так что давайте сделаем это.

Инициализировать редактор Quill

После настройки нашего Quill нам нужно его инициализировать. Обратите внимание: поскольку я загружаю свой контент из красноречивой модели Laravel, здесь я использую синтаксис блейда Laravel: {!! $post->content !!} .

            var content = new Quill(document.createElement("div"), {
                theme: 'snow',
                readonly: false,
                modules: {
                    syntax: false,
                    toolbar: '',
                },
            });

            content.setContents({!! $post->content !!});

Найти заголовки и исправить их идентификаторы

Теперь нам нужно перебрать ops и найти теги заголовков. Массив headers содержит теги заголовков, которые мы нашли в операциях, за исключением заголовков, которые содержат только пустую строку ( \n ), потому что иногда теги заголовков включают пустые строки.

Теперь пришло время создать нашу оглавление. Мы можем выполнить это с помощью функции Quill insertText и добавить каждый заголовок в список (formatLine). Мы также заменяем пробелы в тексте заголовка на тире.

Контент готов с оглавлением в начале, поэтому давайте покажем его с помощью $(“#q-content”).html(content.root.innerHTML).

            let index = 0;
            let headers = [];

            content.getContents().ops.forEach(ops => {
                index++;

                if (ops?.attributes?.header) {
                    if (content.getContents().ops[index - 2].insert !== "\n") {
                        headers.push(content.getContents().ops[index - 2].insert);
                    }
                }
            });

            headers.slice().reverse().forEach(h => {
                content.insertText(0, h + '\n', 'link', '#' + h.replace(/\s/g , "-"), {
                    'color': '#5a3e3e',
                    'italic': true
                });
                content.formatLine(1, 1, 'list', 'bullet');
            })

            $("#q-content").html(content.root.innerHTML);

Наконец, мы обновляем случайно созданные идентификаторы их новыми значениями:

            ids.forEach(hid => {
                try {
                    var node = document.getElementById(hid);
                    node.id = node.innerHTML.replace(/\s/g , "-");
                    
                } catch (error) {
                    
                }
            });

Заключительные слова

Вот и все. У нас есть пост с оглавлением с использованием текстового редактора Quill и некоторых кодов JavaScript. Хотя это прямое и простое решение для создания TOC для Quill, я уверен, что есть много возможностей для его улучшения, и, конечно же, его можно закодировать более надежно и профессионально. Поэтому не стесняйтесь улучшать его и дайте мне знать, как вы с этим справляетесь. Свяжитесь со мной в случае возникновения вопросов.