六一的部落格


关关难过关关过,前路漫漫亦灿灿。




说明

  1. Hugo是提供文章目录内容的
    1{{ .TableOfContents }}
  2. 限制
    • 文章目录支持折叠
    • 指定级别标题

      Hugo的标题只有2-4
    • 标题重名

      仍未解决
  3. 将文章目录填写到Bootstrap 5侧边折叠导航

    RUNOOB - Bootstrap5 - 侧边栏导航
  4. 标题样式

    RUNOOB - Bootstrap5 - 下拉菜单
  5. 设计
    • 点击标题跳转到文章是保证了的
    • 展开/折叠子标题需点击可折叠标题末尾的按钮

侧边折叠导航结构

  1. 最外层为

    1<div><ul>中间</ul></div>  
  2. 无子标题结构为

    1<li><a href="#"><span></span>heading 1</a></li>

    其中, span会显示一个圆点标识行, 以及该标题是否可折叠

  3. 有子标题结构为

    • li 由三部分组成: a , button , dib
    • a 为标题链接
    • 点击按钮可折叠/取消折叠子标题
    • div 往下是 ul , 之后是子标题
     1<li>
     2  <a href="#"><span></span>heading 2</a>
     3  <button></button>
     4  <div>
     5    <ul>
     6      <li><a href="#"><span></span>heading 2 - 1</a></li>
     7      <li><a href="#"><span></span>heading 2 - 2</a>
     8      </li>
     9    </ul>
    10  </div>
    11</li>
 1<div>
 2  <ul>
 3    <li><a href="#"><span></span>heading 1</a></li>
 4    <li>
 5      <a href="#"><span></span>heading 2</a>
 6      <button></button>
 7      <div>
 8        <ul>
 9          <li><a href="#"><span></span>heading 2 - 1</a></li>
10          <li><a href="#"><span></span>heading 2 - 2</a>
11          </li>
12        </ul>
13      </div>
14    </li>
15    <li><a href="#><span></span>heading 3</a></li>
16  </ul>
17</div>

思路

  1. 遍历逻辑前后添加最外层
  2. 每一个标题是否可折叠, 通过其标题大小和下一个标题的大小相比较作出判断; 这其中还涉及是否要关闭非叶子标题
    • 当前标题大: 可折叠
    • 二者相等: 不可折叠
    • 后者标题大: 不可折叠; 关闭上级标题
  3. 最后一个标题: 不可折叠, 关闭上级标题

折叠关键参数

  1. data-bs-target

    折叠按钮信息, button元素使用
  2. data-bs-toggle

    用于可折叠标题的下级div; 可折叠标题最外层为li

    点击按钮时, 折叠或展开div下的元素

    与data-bs-target相关联: data-bs-target为 #id , data-bs-toggle则为 id
  3. collapse

    可折叠标题的下级div使用; 默认折叠

    为div加上 show , 默认展开
  4. aria-expanded

    用于button, 关系到折叠图片的旋转

    为false, 图标尖尖朝左; 为true, 图标尖尖朝下

    与div是否有show相关联: div有show, 默认展开, 则应为true; div无show, 默认折叠, 则应为false

注意项

  1. html代码里的双引号需要转义
  2. 为了保证代码清晰, 以及一定程度的复用, 灵活使用传参和封装
  3. 自用, 一定程度上, 配置有些繁琐

接口

  1. 封装属性
    1function format_attribute(attribute, content)
    2{
    3    temp = attribute;
    4    temp += "=\"" + content + "\"";
    5    return temp;
    6}
  2. 封装span
    1function format_span(circle_color)
    2{
    3    temp = "<span ";
    4    temp += format_attribute("class", "d-inline-block rounded-circle " + circle_color);
    5    temp += format_attribute("style", "width: .5em; height: .5em; flex-shrink: 0 ");
    6    temp += "></span>";
    7
    8    return temp;
    9}
  3. 封装叶子标题a
     1function format_a(heading, class_content, descript, circle_color)
     2{
     3    temp = "<a ";
     4
     5    href_content = "#" + heading;
     6    temp += format_attribute("href", href_content);
     7
     8    temp += " ";
     9    temp += format_attribute("class", class_content);
    10    temp += ">";
    11    temp += format_span(circle_color);
    12    temp += " &nbsp " + descript;
    13    temp += "</a>";
    14
    15    return temp;
    16}
  4. 封装叶子标题li
     1function format_li(heading, descript, circle_color)
     2{
     3    temp = "<li ";
     4    temp += format_attribute("class", "mb-1 my-1 ms-3");
     5    temp += ">";
     6    temp += format_a(heading, "rounded align-items-baseline", descript, circle_color);
     7    temp += "</li>";
     8
     9    return temp;
    10}
  5. 封装非叶子标题li开头
    1function format_li_start(class_content)
    2{
    3    temp = "<li ";
    4    temp += format_attribute("class", class_content);
    5    temp += ">";
    6
    7    return temp;
    8}
  6. 封装非叶子标题button
     1function format_button_toogle(class_content, if_toggle, toggle_target, expanded_value)
     2{
     3
     4    temp = "<button ";
     5    temp += format_attribute("class", class_content);
     6
     7    temp += " ";
     8    temp += format_attribute("data-bs-toggle", if_toggle);
     9
    10    temp += " ";
    11    temp += format_attribute("data-bs-target", toggle_target);
    12
    13    temp += " ";
    14    temp += format_attribute("aria-expanded", expanded_value);
    15
    16    temp += "></button>";
    17
    18    return temp;
    19}
  7. 封装非叶子标题a
     1function format_button_a(descript, a_class, heading)
     2{
     3    temp = "<a ";
     4
     5    href_content = "#" + heading;
     6    temp += format_attribute("href", href_content);
     7
     8    temp += " ";
     9    temp += format_attribute("class", a_class + " " +
    10                                          "align-items-baseline");
    11
    12    temp += ">";
    13
    14    temp += format_span("bg-success");
    15    temp += " &nbsp " + descript;
    16
    17    temp += "</a>";
    18    return temp;
    19}
  8. 封装非叶子标题div开头
     1function format_div_start(id)
     2{
     3    temp = "<div ";
     4
     5    /* 默认不展开, 添加show可以展开 */
     6    temp += format_attribute("class", "collapse");
     7
     8    temp += " ";
     9    temp += format_attribute("id", id);
    10
    11    temp += ">";
    12
    13    return temp;
    14}
  9. 封装非叶子标题ul开头
     1function format_ul_start(class_content)
     2{
     3    temp = "<ul ";
     4
     5    temp += format_attribute("class", class_content);
     6
     7    temp += ">";
     8
     9    return temp;
    10}
  10. 封装非叶子标题开头
     1function button_pack_start(heading, descript)
     2{
     3    temp = format_li_start("mb-1 my-1 ms-3");
     4
     5    toggle_target_id = "toc-" + heading;
     6    toggle_target = "#" + toggle_target_id;
     7
     8    a_class = "rounded";
     9
    10    temp += format_button_a(descript, a_class, heading);
    11
    12    temp += format_button_toogle("toc-btn-toggle rounded collapsed", "collapse", toggle_target, "false");
    13
    14    temp += format_div_start(toggle_target_id);
    15
    16    temp += format_ul_start("btn-toggle-nav list-unstyled fw-normal pb-1");
    17
    18    return temp;
    19}
  11. 封装非叶子标题结尾
    1function button_pack_close() /* close_button */
    2{
    3    temp = "";
    4    temp += "</ul>";
    5    temp += "</div>";
    6    temp += "</li>";
    7    return temp;
    8}
  12. 传入标题数组, 返回文章目录
     1function format_toc(headings)
     2{
     3    toc = "<div class=\"flex-shrink-0 p-3 toc-sticky\">\n";
     4    toc += "<ul class=\"list-unstyled ps-0\">\n"
     5
     6    for (i = 1; i < headings.length; ++i)
     7    {
     8        tag_next = parseInt(headings[i].tagName[1]);
     9
    10        heading = headings[i - 1];
    11        // console.log(heading.innerText.replace(" #", ""));
    12
    13        id = heading.getAttribute('id');
    14        tag = parseInt(heading.tagName[1]);
    15        descript = heading.innerText.replace(" #", "");
    16
    17        if (tag_next > tag)
    18        {
    19            // toc += format_button(id);
    20            toc += button_pack_start(id, descript);
    21        }
    22        else
    23        {
    24            // toc += format_li(id);
    25            toc += format_li(id, descript, "bg-primary") + "\n";
    26            if (tag_next < tag)
    27            {
    28                for (j = 0; j < tag - tag_next; ++j)
    29                {
    30                    // toc += close_button();
    31                    toc += button_pack_close() + "\n";
    32                }
    33            }
    34        }
    35    }
    36
    37    heading = headings[headings.length - 1];
    38    id = heading.getAttribute('id');
    39    tag = parseInt(heading.tagName[1]);
    40    heading.innerText.replace(" #", "");
    41
    42    // toc += format_li(id_next);
    43    toc += format_li(id, descript, "bg-primary");
    44    for (j = 0; j < tag - 2; ++j)
    45    {
    46        // toc += close_button();
    47        toc += button_pack_close() + "\n";
    48    }
    49
    50    toc += "</ul></div>\n";
    51
    52    // console.log(toc);
    53
    54    return toc;
    55}
  13. 获取页面指定大小标题, 往页面写入文章目录
    1function add_toc()
    2{
    3    const headings = Array.apply(null, document.querySelectorAll('h2[id], h3[id], h4[id]'))
    4                         .filter(function(value, index, arr) { return arr[index].querySelector('.anchor'); });
    5
    6    const toc = format_toc(headings);
    7    document.write(toc);
    8}

函数, 属性和样式

  1. span - CSS样式

    作为a的下级元素, 解决标题名过长圆点会被压缩的问题

    1flex-shink: 0
  2. a - CSS样式

    align-items-baseline

    a的下级元素的对齐方式

  3. span - CSS样式

    Bootstrap 5 提供的背景色

    -
    bg-success 绿色 非叶子标题
    bg-primary 蓝色 叶子标题
  4. ul - CSS样式

    list-unstyled

    a前面无圆点

  5. a - CSS样式

    toc-btn-toggle

    自定义样式, 其中设置文本分行较为重要

    1word-wrap: break-word;
  6. Html和JavaScript

    -
    TagName 标签类型, 如h2, h5
    innerText 标题内文本
    JS: string replace 文本替换
    document.write 在执行脚本处写入

便签

-
Bootstrap5 - 折叠
JavaScript创建元素
将a用作按钮
Bootstrap 5 - 按钮
JS判断元素是否有下级
元素不被压缩
JS - replace
CSS - align-items
Bootstrap5 - 颜色
html空格
自动换行

JavaScript - 爬取文章目录



说明

  1. Hugo是提供文章目录内容的
    1{{ .TableOfContents }}
  2. 限制
    • 文章目录支持折叠
    • 指定级别标题

      Hugo的标题只有2-4
    • 标题重名

      仍未解决
  3. 将文章目录填写到Bootstrap 5侧边折叠导航

    RUNOOB - Bootstrap5 - 侧边栏导航
  4. 标题样式

    RUNOOB - Bootstrap5 - 下拉菜单
  5. 设计
    • 点击标题跳转到文章是保证了的
    • 展开/折叠子标题需点击可折叠标题末尾的按钮

侧边折叠导航结构

  1. 最外层为

    1<div><ul>中间</ul></div>  
  2. 无子标题结构为

    1<li><a href="#"><span></span>heading 1</a></li>

    其中, span会显示一个圆点标识行, 以及该标题是否可折叠

  3. 有子标题结构为

    • li 由三部分组成: a , button , dib
    • a 为标题链接
    • 点击按钮可折叠/取消折叠子标题
    • div 往下是 ul , 之后是子标题
     1<li>
     2  <a href="#"><span></span>heading 2</a>
     3  <button></button>
     4  <div>
     5    <ul>
     6      <li><a href="#"><span></span>heading 2 - 1</a></li>
     7      <li><a href="#"><span></span>heading 2 - 2</a>
     8      </li>
     9    </ul>
    10  </div>
    11</li>
 1<div>
 2  <ul>
 3    <li><a href="#"><span></span>heading 1</a></li>
 4    <li>
 5      <a href="#"><span></span>heading 2</a>
 6      <button></button>
 7      <div>
 8        <ul>
 9          <li><a href="#"><span></span>heading 2 - 1</a></li>
10          <li><a href="#"><span></span>heading 2 - 2</a>
11          </li>
12        </ul>
13      </div>
14    </li>
15    <li><a href="#><span></span>heading 3</a></li>
16  </ul>
17</div>

思路

  1. 遍历逻辑前后添加最外层
  2. 每一个标题是否可折叠, 通过其标题大小和下一个标题的大小相比较作出判断; 这其中还涉及是否要关闭非叶子标题
    • 当前标题大: 可折叠
    • 二者相等: 不可折叠
    • 后者标题大: 不可折叠; 关闭上级标题
  3. 最后一个标题: 不可折叠, 关闭上级标题

折叠关键参数

  1. data-bs-target

    折叠按钮信息, button元素使用
  2. data-bs-toggle

    用于可折叠标题的下级div; 可折叠标题最外层为li

    点击按钮时, 折叠或展开div下的元素

    与data-bs-target相关联: data-bs-target为 #id , data-bs-toggle则为 id
  3. collapse

    可折叠标题的下级div使用; 默认折叠

    为div加上 show , 默认展开
  4. aria-expanded

    用于button, 关系到折叠图片的旋转

    为false, 图标尖尖朝左; 为true, 图标尖尖朝下

    与div是否有show相关联: div有show, 默认展开, 则应为true; div无show, 默认折叠, 则应为false

注意项

  1. html代码里的双引号需要转义
  2. 为了保证代码清晰, 以及一定程度的复用, 灵活使用传参和封装
  3. 自用, 一定程度上, 配置有些繁琐

接口

  1. 封装属性
    1function format_attribute(attribute, content)
    2{
    3    temp = attribute;
    4    temp += "=\"" + content + "\"";
    5    return temp;
    6}
  2. 封装span
    1function format_span(circle_color)
    2{
    3    temp = "<span ";
    4    temp += format_attribute("class", "d-inline-block rounded-circle " + circle_color);
    5    temp += format_attribute("style", "width: .5em; height: .5em; flex-shrink: 0 ");
    6    temp += "></span>";
    7
    8    return temp;
    9}
  3. 封装叶子标题a
     1function format_a(heading, class_content, descript, circle_color)
     2{
     3    temp = "<a ";
     4
     5    href_content = "#" + heading;
     6    temp += format_attribute("href", href_content);
     7
     8    temp += " ";
     9    temp += format_attribute("class", class_content);
    10    temp += ">";
    11    temp += format_span(circle_color);
    12    temp += " &nbsp " + descript;
    13    temp += "</a>";
    14
    15    return temp;
    16}
  4. 封装叶子标题li
     1function format_li(heading, descript, circle_color)
     2{
     3    temp = "<li ";
     4    temp += format_attribute("class", "mb-1 my-1 ms-3");
     5    temp += ">";
     6    temp += format_a(heading, "rounded align-items-baseline", descript, circle_color);
     7    temp += "</li>";
     8
     9    return temp;
    10}
  5. 封装非叶子标题li开头
    1function format_li_start(class_content)
    2{
    3    temp = "<li ";
    4    temp += format_attribute("class", class_content);
    5    temp += ">";
    6
    7    return temp;
    8}
  6. 封装非叶子标题button
     1function format_button_toogle(class_content, if_toggle, toggle_target, expanded_value)
     2{
     3
     4    temp = "<button ";
     5    temp += format_attribute("class", class_content);
     6
     7    temp += " ";
     8    temp += format_attribute("data-bs-toggle", if_toggle);
     9
    10    temp += " ";
    11    temp += format_attribute("data-bs-target", toggle_target);
    12
    13    temp += " ";
    14    temp += format_attribute("aria-expanded", expanded_value);
    15
    16    temp += "></button>";
    17
    18    return temp;
    19}
  7. 封装非叶子标题a
     1function format_button_a(descript, a_class, heading)
     2{
     3    temp = "<a ";
     4
     5    href_content = "#" + heading;
     6    temp += format_attribute("href", href_content);
     7
     8    temp += " ";
     9    temp += format_attribute("class", a_class + " " +
    10                                          "align-items-baseline");
    11
    12    temp += ">";
    13
    14    temp += format_span("bg-success");
    15    temp += " &nbsp " + descript;
    16
    17    temp += "</a>";
    18    return temp;
    19}
  8. 封装非叶子标题div开头
     1function format_div_start(id)
     2{
     3    temp = "<div ";
     4
     5    /* 默认不展开, 添加show可以展开 */
     6    temp += format_attribute("class", "collapse");
     7
     8    temp += " ";
     9    temp += format_attribute("id", id);
    10
    11    temp += ">";
    12
    13    return temp;
    14}
  9. 封装非叶子标题ul开头
     1function format_ul_start(class_content)
     2{
     3    temp = "<ul ";
     4
     5    temp += format_attribute("class", class_content);
     6
     7    temp += ">";
     8
     9    return temp;
    10}
  10. 封装非叶子标题开头
     1function button_pack_start(heading, descript)
     2{
     3    temp = format_li_start("mb-1 my-1 ms-3");
     4
     5    toggle_target_id = "toc-" + heading;
     6    toggle_target = "#" + toggle_target_id;
     7
     8    a_class = "rounded";
     9
    10    temp += format_button_a(descript, a_class, heading);
    11
    12    temp += format_button_toogle("toc-btn-toggle rounded collapsed", "collapse", toggle_target, "false");
    13
    14    temp += format_div_start(toggle_target_id);
    15
    16    temp += format_ul_start("btn-toggle-nav list-unstyled fw-normal pb-1");
    17
    18    return temp;
    19}
  11. 封装非叶子标题结尾
    1function button_pack_close() /* close_button */
    2{
    3    temp = "";
    4    temp += "</ul>";
    5    temp += "</div>";
    6    temp += "</li>";
    7    return temp;
    8}
  12. 传入标题数组, 返回文章目录
     1function format_toc(headings)
     2{
     3    toc = "<div class=\"flex-shrink-0 p-3 toc-sticky\">\n";
     4    toc += "<ul class=\"list-unstyled ps-0\">\n"
     5
     6    for (i = 1; i < headings.length; ++i)
     7    {
     8        tag_next = parseInt(headings[i].tagName[1]);
     9
    10        heading = headings[i - 1];
    11        // console.log(heading.innerText.replace(" #", ""));
    12
    13        id = heading.getAttribute('id');
    14        tag = parseInt(heading.tagName[1]);
    15        descript = heading.innerText.replace(" #", "");
    16
    17        if (tag_next > tag)
    18        {
    19            // toc += format_button(id);
    20            toc += button_pack_start(id, descript);
    21        }
    22        else
    23        {
    24            // toc += format_li(id);
    25            toc += format_li(id, descript, "bg-primary") + "\n";
    26            if (tag_next < tag)
    27            {
    28                for (j = 0; j < tag - tag_next; ++j)
    29                {
    30                    // toc += close_button();
    31                    toc += button_pack_close() + "\n";
    32                }
    33            }
    34        }
    35    }
    36
    37    heading = headings[headings.length - 1];
    38    id = heading.getAttribute('id');
    39    tag = parseInt(heading.tagName[1]);
    40    heading.innerText.replace(" #", "");
    41
    42    // toc += format_li(id_next);
    43    toc += format_li(id, descript, "bg-primary");
    44    for (j = 0; j < tag - 2; ++j)
    45    {
    46        // toc += close_button();
    47        toc += button_pack_close() + "\n";
    48    }
    49
    50    toc += "</ul></div>\n";
    51
    52    // console.log(toc);
    53
    54    return toc;
    55}
  13. 获取页面指定大小标题, 往页面写入文章目录
    1function add_toc()
    2{
    3    const headings = Array.apply(null, document.querySelectorAll('h2[id], h3[id], h4[id]'))
    4                         .filter(function(value, index, arr) { return arr[index].querySelector('.anchor'); });
    5
    6    const toc = format_toc(headings);
    7    document.write(toc);
    8}

函数, 属性和样式

  1. span - CSS样式

    作为a的下级元素, 解决标题名过长圆点会被压缩的问题

    1flex-shink: 0
  2. a - CSS样式

    align-items-baseline

    a的下级元素的对齐方式

  3. span - CSS样式

    Bootstrap 5 提供的背景色

    -
    bg-success 绿色 非叶子标题
    bg-primary 蓝色 叶子标题
  4. ul - CSS样式

    list-unstyled

    a前面无圆点

  5. a - CSS样式

    toc-btn-toggle

    自定义样式, 其中设置文本分行较为重要

    1word-wrap: break-word;
  6. Html和JavaScript

    -
    TagName 标签类型, 如h2, h5
    innerText 标题内文本
    JS: string replace 文本替换
    document.write 在执行脚本处写入

便签

-
Bootstrap5 - 折叠
JavaScript创建元素
将a用作按钮
Bootstrap 5 - 按钮
JS判断元素是否有下级
元素不被压缩
JS - replace
CSS - align-items
Bootstrap5 - 颜色
html空格
自动换行