Подтвердить что ты не робот

Есть ли решение JavaScript для создания "оглавления" для страницы?

У меня есть заголовки в тегах <h1> через <h6>. Есть ли способ, которым я могу использовать JavaScript для создания оглавления для содержимого, которое также служит тегом привязки?

Я хотел бы, чтобы результат был чем-то вроде:

<ol>
    <li>Header 1</li>
    <li>Header 1</li>
        <li>Header 2</li>
            <li>Header 3</li>
</ol>

В настоящее время я не использую фреймворк JavaScript, но я не понимаю, почему я не мог его использовать.

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

4b9b3361

Ответ 1

Я не мог удержаться от быстрой реализации.

Добавьте следующий script в любом месте вашей страницы:

window.onload = function () {
    var toc = "";
    var level = 0;

    document.getElementById("contents").innerHTML =
        document.getElementById("contents").innerHTML.replace(
            /<h([\d])>([^<]+)<\/h([\d])>/gi,
            function (str, openLevel, titleText, closeLevel) {
                if (openLevel != closeLevel) {
                    return str;
                }

                if (openLevel > level) {
                    toc += (new Array(openLevel - level + 1)).join("<ul>");
                } else if (openLevel < level) {
                    toc += (new Array(level - openLevel + 1)).join("</ul>");
                }

                level = parseInt(openLevel);

                var anchor = titleText.replace(/ /g, "_");
                toc += "<li><a href=\"#" + anchor + "\">" + titleText
                    + "</a></li>";

                return "<h" + openLevel + "><a name=\"" + anchor + "\">"
                    + titleText + "</a></h" + closeLevel + ">";
            }
        );

    if (level) {
        toc += (new Array(level + 1)).join("</ul>");
    }

    document.getElementById("toc").innerHTML += toc;
};

Ваша страница должна быть структурирована примерно так:

<body>
    <div id="toc">
        <h3>Table of Contents</h3>
    </div>
    <hr/>
    <div id="contents">
        <h1>Fruits</h1>
        <h2>Red Fruits</h2>
        <h3>Apple</h3>
        <h3>Raspberry</h3>
        <h2>Orange Fruits</h2>
        <h3>Orange</h3>
        <h3>Tangerine</h3>
        <h1>Vegetables</h1>
        <h2>Vegetables Which Are Actually Fruits</h2>
        <h3>Tomato</h3>
        <h3>Eggplant</h3>
    </div>
</body>

Вы можете увидеть его в действии на http://magnetiq.com/exports/toc.htm (работает в IE, FF, Safari, Opera)

Ответ 3

Здесь большой script для этого:

https://github.com/matthewkastor/html-table-of-contents/wiki

Чтобы использовать его:

  • Добавьте этот тег:

    <script src="./node_modules/html-table-of-contents/src/html-table-of-contents.js" type="text/javascript">
    
  • Вызовите функцию, например, в свой собственный атрибут onload:

    <body onload="htmlTableOfContents();"> 
    

Вот определение метода, который генерирует:

/**
 * Generates a table of contents for your document based on the headings
 *  present. Anchors are injected into the document and the
 *  entries in the table of contents are linked to them. The table of
 *  contents will be generated inside of the first element with the id `toc`.
 * @param {HTMLDOMDocument} documentRef Optional A reference to the document
 *  object. Defaults to `document`.
 * @author Matthew Christopher Kastor-Inare III
 * @version 20130726
 * @example
 * // call this after the page has loaded
 * htmlTableOfContents();
 */
function htmlTableOfContents (documentRef) {
    var documentRef = documentRef || document;
    var toc = documentRef.getElementById('toc');
    var headings = [].slice.call(documentRef.body.querySelectorAll('h1, h2, h3, h4, h5, h6'));
    headings.forEach(function (heading, index) {
        var anchor = documentRef.createElement('a');
        anchor.setAttribute('name', 'toc' + index);
        anchor.setAttribute('id', 'toc' + index);

        var link = documentRef.createElement('a');
        link.setAttribute('href', '#toc' + index);
        link.textContent = heading.textContent;

        var div = documentRef.createElement('div');
        div.setAttribute('class', heading.tagName.toLowerCase());

        div.appendChild(link);
        toc.appendChild(div);
        heading.parentNode.insertBefore(anchor, heading);
    });
}

try {
     module.exports = htmlTableOfContents;
} catch (e) {
    // module.exports is not defined
}

Ответ 4

Вы ищете готовое решение или спрашиваете, как это можно реализовать?

Для последнего вы можете использовать getElementsByTagName() рекурсивно на <h1> через <h6> XPath для повторения всех элементов <h*> и построить соответствующие вложенные <ul> или <ol> списки. Вам также нужно добавить теги <a> в заголовки.

Ответ 5

Я изменил функцию в принятом AtesGoral ответе на вывод правильно вложенных списков и допустимый HTML5.

Просто добавьте приведенный ниже код в свои сценарии и вызовите TableOfContents(container, output); при загрузке, где контейнер является классом или идентификатором вашего элемента контента, а output - это класс или идентификатор элемента TOC. Значения по умолчанию: "#contents" и "#toc" соответственно.

См. http://codepen.io/aufmkolk/pen/RWKLzr для рабочей демонстрации.

function TableOfContents(container, output) {
var toc = "";
var level = 0;
var container = document.querySelector(container) || document.querySelector('#contents');
var output = output || '#toc';

container.innerHTML =
    container.innerHTML.replace(
        /<h([\d])>([^<]+)<\/h([\d])>/gi,
        function (str, openLevel, titleText, closeLevel) {
            if (openLevel != closeLevel) {
                return str;
            }

            if (openLevel > level) {
                toc += (new Array(openLevel - level + 1)).join('<ul>');
            } else if (openLevel < level) {
                toc += (new Array(level - openLevel + 1)).join('</li></ul>');
            } else {
                toc += (new Array(level+ 1)).join('</li>');
            }

            level = parseInt(openLevel);

            var anchor = titleText.replace(/ /g, "_");
            toc += '<li><a href="#' + anchor + '">' + titleText
                + '</a>';

            return '<h' + openLevel + '><a href="#' + anchor + '" id="' + anchor + '">'
                + titleText + '</a></h' + closeLevel + '>';
        }
    );

if (level) {
    toc += (new Array(level + 1)).join('</ul>');
}
document.querySelector(output).innerHTML += toc;
};

Ответ 6

 let headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
  let list    = document.createElement('ol');

  let _el = list;
  for(i=0; i<headers.length; i++) {
    while(_el) {
      let li = document.createElement('li');
      li.innerText = headers[i].innerText;
      li.setAttribute('tagName', headers[i].tagName);
      if(_el.getAttribute('tagName') < headers[i].tagName) {
        let ol = _el.children.length > 0 ? ol = _el.querySelector('ol') : document.createElement('ol');
        ol.appendChild(li);
        _el.appendChild(ol);
        _el = li;
        break;
      } else {
        if(_el.tagName == 'OL') {
         _el.appendChild(li);
         _el = li;
         break;
        } else if (!_el.parentNode.parentNode) {
          _el.parentNode.appendChild(li);
          _el = li;
          break;
        }
        else {
          _el = _el.parentNode.parentNode;
        }
      }
    }
  }
  console.log(list);

Ответ 7

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

Ответ 8

Посмотрите компонент, который вы ищете на этой странице: Повторное создание XMLHttpRequest: кросс-браузерная реализация с возможностями нюхания

Он просматривает весь документ и создает TOC со всеми элементами h1-h6, отраженными в структуре openable (on hover). Компонент является автономным и не использует библиотеку.

Ответ 9

Вы можете создать динамическое оглавление для любого HTML-документа с помощью JavaScript, который может отображать список заголовков от h1 до h6 со ссылками на заголовки и упростить навигацию по документу, используя следующие шаги.

Сначала создайте функцию window.onload, которая автоматически запускается, когда документ заканчивает загрузку, как указано ниже.

window.onload=function(){

function getSelectedText(){
if (window.getSelection)
return window.getSelection().toString()+"
"+document.URL;
else if (document.selection)
 return document.selection.createRange().text+"
"+document.URL;
}
var toc=document.getElementById("TOC");
if(!toc) {
 toc=document.createElement("div");
 toc.id="TOC";
 document.body.insertBefore(toc, document.body.firstChild);
}

Ответ 10

  this.insert = (el, h) => {
    let li = document.createElement('li');
    li.innerText = h.innerText;
    li.setAttribute('tagName', h.tagName);
    if(el.tagName == 'OL') {
      el.appendChild(li);
      return li;
    } else if(el.getAttribute('tagName') < h.tagName) {
      let ol = el.children.length > 0 ? ol = el.querySelector('ol') : document.createElement('ol');
      ol.appendChild(li);
      el.appendChild(ol);
      return li;
    } else if(!el.parentNode.parentNode) {
      el.parentNode.appendChild(li);
      return li;
    } else {
      return this.insert(el.parentNode.parentNode, h);
    }
  }

  this.parse = (headers) => {
    let list = document.createElement('ol');
    let el = list;
    for(i=0; i<headers.length; i++) {
      el = this.insert(el, headers[i]);
    }
    return list;
  }
  let headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
  let toc = this.parse(headers);
  console.log(toc);

Ответ 11

Мне действительно нравится ответ Sridhar-Sarnobat, но хотел бы немного улучшить его, чтобы использовать нотации html5 и сохранить заголовок существующего id:

document.addEventListener('DOMContentLoaded', function() {
    htmlTableOfContents();
} );                        

function htmlTableOfContents( documentRef ) {
    var documentRef = documentRef || document;
    var toc = documentRef.getElementById("toc");
//  Use headings inside <article> only:
//  var headings = [].slice.call(documentRef.body.querySelectorAll('article h1, article h2, article h3, article h4, article h5, article h6'));
    var headings = [].slice.call(documentRef.body.querySelectorAll('h1, h2, h3, h4, h5, h6'));
    headings.forEach(function (heading, index) {
        var ref = "toc" + index;
        if ( heading.hasAttribute( "id" ) ) 
            ref = heading.getAttribute( "id" );
        else
            heading.setAttribute( "id", ref );

        var link = documentRef.createElement( "a" );
        link.setAttribute( "href", "#"+ ref );
        link.textContent = heading.textContent;

        var div = documentRef.createElement( "div" );
        div.setAttribute( "class", heading.tagName.toLowerCase() );
        div.appendChild( link );
        toc.appendChild( div );
    });
}

try {
    module.exports = htmlTableOfContents;
} catch (e) {
    // module.exports is not defined
}

Вы используете его, включив script в свой заголовок.

Удивительно, вы можете использовать таблицы стилей для вашего оглавления:

<style>
    #toc div.h1 { margin-left: 0 }
    #toc div.h2 { margin-left: 1em }
    #toc div.h3 { margin-left: 2em }
    #toc div.h4 { margin-left: 3em }
</style>

В моем личном script, я использую несколько иной селектор:

var headings = [].slice.call(documentRef.body.querySelectorAll("article h1, article h2, article h3, article h4, article h5, h6"));

Основная страница хранится внутри <article></article>, а script будет искать заголовки только в основной статье. Я могу использовать заголовок внутри оглавления, например <nav id="toc"><h3>Table of contents</h3></nav> или в нижнем колонтитуле/заголовке без его появления внутри toc.