在没有 class 或 id 标签的情况下添加/前缀文本以列出项目

Prepend / prefix text to list items without class or id tags

我正在使用 Docsify.js markdown parser framework,它会根据文档中的标题自动创建侧边栏(除非您手动创建侧边栏)。

我有 some CSS that numbers list elements,但想将其转换为 JS,因为在页面滚动时添加 类 时存在渲染问题(即添加 .active)。

最初,我正在尝试使用此代码段,但它没有将其输出为自动递增的分层数字系统:

生成的边栏格式如下:

var li = document.getElementsByTagName( 'li' );
for( var i = 0; i < li.length; i++ ) {
  var prefix = '1.';
  li[i].innerHTML = prefix + ' Title ' + i;
  prefix++;
}
<aside class="sidebar">
  <div class="sidebar-nav">
    <ul>
      <li>Title 1</li>
      <ul>
        <li>Title 2</li>
        <ul>
          <li>Title 3</li>
          <ul>
            <li>Title 4</li>
            <ul>
              <li>Title 5</li>
              <ul>
                <li>Title 6</li>
              </ul>
            </ul>
          </ul>
        </ul>
      </ul>
      <li>Title 1</li>
      <li>Title 1</li>
      <ul>
        <li>Title 2</li>
        <li>Title 2</li>
        <ul>
          <li>Title 3</li>
          <ul>
            <li>Title 4</li>
            <ul>
              <li>Title 5</li>
              <ul>
                <li>Title 6</li>
              </ul>
            </ul>
          </ul>
        </ul>
      </ul>
    </ul>
  </div>
</aside>

我知道 HTML 结构无效,因为 <ul><ul> 的后代,但这是输出的代码,我无法控制它。

但是,我希望能够用章节对标题进行编号 sub-sections:

1. Title 1
 1.1. Title 2
  1.1.1. Title 3
   1.1.1.1. Title 4
    1.1.1.1.1. Title 5
     1.1.1.1.1.1. Title 6
2. Title 1
3. Title 1
 3.1. Title 2
 3.2. Title 2
  3.2.1. Title 3
   3.2.1.1. Title 4
    3.2.1.1.1. Title 5
     3.2.1.1.1.1. Title 6

我正在努力寻找一种方法来定位第一个 <li>(或 H1),然后能够通过 .nextElementSibling 访问下一个 <ul>继续循环并添加编号。

据我目前所了解的是:document.querySelectorAll( 'div.sidebar-nav > ul' ) 而且没有什么可以继续的了!

我想我真的无法理解 javascript 这里的内容,希望我能够在循环遍历 <li><ul> 元素添加到数字前。

以下是JavaScript应用嵌套索引号。最多只有6个header标签,6个级别,所以我们可以使用递归解决方案:

let startLevel = 1;
let endLevel = 5;

function indexsify() {
  let children = document.querySelectorAll('#sidebar > ul');
  let numbers = new Array(7).fill(0);
  let depth = 0;

  children.forEach((element, index) => {
    recurse(element, ++depth, numbers);
  });
}

function recurse(element, depth, numbers) { //ul
  let children = Array.from(element.children);

  children.forEach((element, index) => {
    if (element.localName.toUpperCase() === 'LI') {
      numbers[depth]++;
      addNumberString(element, depth, numbers);

    } else if (element.localName.toUpperCase() === 'UL') {
      if (depth < endLevel) {
        recurse(element, depth + 1, numbers, startLevel);

        numbers.fill(0, depth + 1); //reset all next numbers
      }
    }
  });
}

function addNumberString(element, depth, numbers) {
  let strNumber = "";
  numbers.forEach((num, index) => {
    if (index > depth || index <= startLevel) return;
    strNumber += `${num}.`;
  });
  element.firstElementChild.innerText = strNumber +
    element.firstElementChild.innerText;
}

indexsify();
ul,
li {
  list-style-type: none;
}
<div id="sidebar">
  <ul>
    <li><a>Home</a></li>
    <ul>
      <li><a>Chapter a</a></li>
      <ul>
        <li><a> Section a</a></li>
        <li><a>Section b</a></li>
      </ul>
      <li><a>Chapter b</a></li>
      <li><a>Chapter c</a></li>
      <ul>
        <li><a>Section a</a></li>
        <li><a>Section b</a></li>
        <ul>
          <li><a>Sub-section a</a></li>
        </ul>
      </ul>
      <li><a>Chapter D</a></li>
    </ul>
  </ul>
</div>


修改 markdown 本身: 根据 Docsify plugin documentation 没有直接规定来影响边栏内容。您的插件使用 hook.afterEach(function(html, next) 并且侧边栏是单独生成的。所以你也试图操纵生成的侧边栏。您正在尝试执行两次类似的操作。
为什么不使用 hook.beforeEach(function(content) 并自己操作 markdown。这样,您只需执行一次编号操作。
这是一个 demo site and the code sandbox link 用于以下操作降价内容的示例插件:

<!DOCTYPE html>
<html>
  <body>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/docsify@4/themes/vue.css"
    />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/docsify/themes/dark.css"
    />
    <div id="app">Please wait...</div>
    <script>
      window.$docsify = {
        el: "#app",
        loadSidebar: true,
        maxLevel: 4,
        subMaxLevel: 5,
        homepage: "readme.md"
      };
    </script>

    <script>
      //plugin
      let myPlugin = function (hook, vm) {
        hook.init(function () {
          //remove '-' before Table of content entries
          let customStyles = document.createElement("style");
          customStyles.type = "text/css";
          customStyles.textContent = `.app-sub-sidebar li::before {
             content: '' !important;
             padding-right: 4px;
             float: left;
           }`;
          document.body.appendChild(customStyles);
        });

        //update markdown content before docsify parsing
        hook.beforeEach(function (content) {
          let lines = content.split("\n");

          let numbers = new Array(6).fill(0);
          let depth = 0;

          lines.forEach((line, index) => {
            let level = getLevel(line);

            //if not a header continue to next line
            if (level === -1) return;

            if (level > depth) {
              depth++; //increase depth
            } else {
              depth = level; //decrease depth
              numbers.fill(0, depth + 1); //set all next depth to 0
            }
            numbers[depth]++;

            let strNumber = "";
            numbers.forEach((num, index) => {
              if (index > depth || index < startLevel) return;
              strNumber += `${num}.`;
            });

            if (depth < endLevel) {
              lines[index] =
                levels[level] + strNumber + line.substr(depth + 1, line.length);
            }
          });

          //update original content
          content = lines.join("\n");
          return content;
        });

        let levels = ["# ", "## ", "### ", "#### ", "##### ", "###### "];
        let startLevel = 1;
        let endLevel = 4;
        let regEx = new RegExp(`^#{1,${endLevel}}\s+.*`);

        function getLevel(line) {
          if (!regEx.test(line)) return -1; //not a header line
          if (line.startsWith(levels[0])) return 0; //h1
          if (line.startsWith(levels[1])) return 1;
          if (line.startsWith(levels[2])) return 2;
          if (line.startsWith(levels[3])) return 3;
          if (line.startsWith(levels[4])) return 4;
          if (line.startsWith(levels[5])) return 5; //h6
        }
      };

      window.$docsify.plugins = [myPlugin];
    </script>
    <script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
  </body>
</html>

我们需要覆盖 hook.init(function ()) 中的默认值 CSS 以删除内容 table 中的前导 -


旧答案:您可以直接在锚点上添加数字<a> 标签:

.sidebar ul>li {
  counter-increment: item;
}

.sidebar ul>li:first-child {
  counter-reset: item;
}

.sidebar ul>li a::before {
  content: counters(item, ".") " ";
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify@4/themes/vue.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify/themes/dark.css" />
<div id="app">Please wait...</div>
<script>
  window.$docsify = {
    el: "#app",
    loadSidebar: false,
    homepage: 'https://gist.githubusercontent.com/OnkarRuikar/bb1d986f279dddceea9004a4bde3844b/raw/80fe153d6b8c1bb2b8e7035be7df1bb908779061/readme.md'
  }
</script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>

如果您在主要部分为标题生成编号,那么它们可能会自动填充到边栏中。您可以使用 markdown setting to write the script. Or you can try something like: https://github.com/markbattistella/docsify-autoHeaders

您想根据其类型对外部 UL 的每个子级“做某事”——在当前级别递增索引,然后添加一个标签(如果它是 LI);否则递归到下一个级别,如果它是 UL。鉴于 OP 提供的 HTML,这段代码产生了完全期望的结果:

function addLabels(element, prefix) {
    var index = 0;

    Array.from(element.children).forEach(element => {
        if (element.localName.toUpperCase() === 'LI') {
            index += 1;
            element.innerText = prefix + index + '. ' + element.innerText;
        } else if (element.localName.toUpperCase() === 'UL') {
            addLabels(element, prefix + index + '.');
        }
    });
}

document.querySelectorAll('div.sidebar-nav > ul').forEach(
    element => addLabels(element, '')
);

另外,您提到了使用 nextElementSibling 从 LI 获取 UL 的想法。那会起作用,但我认为您最终会得到不太健壮、性能较差且不是特别清晰的代码。但为了记录,这可能看起来像这样:

function addLabels2(element, prefix) {
    Array.from(element.querySelectorAll(':scope > li')).forEach((element, index) => {
            var label = prefix + (index+1) + '.';
            var sibling = element.nextElementSibling;

            element.innerText = label + ' ' + element.innerText;
            if (sibling && sibling.localName.toUpperCase() === 'UL') {
                addLabels2(sibling, label);
            }
    });
}

document.querySelectorAll('div.sidebar-nav > ul').forEach(
    element => addLabels2(element, '')
);