<body>
  <nav id="toc">
  </nav>
  <main>
    <h1>東京</h1>
    <p>
     東京は、日本の関東平野中央部に位置し、東京湾に面する都市。日本の事実上の首都である。日本における政治・経済・文化の中心都市である。現在、その域内には23特別区・26市・5町・8村の基礎自治体がある。
    </p>
    <h2>概要・沿革</h2>
    <p>江戸幕府の所在地であった江戸という都市が慶応4年7月(1868年9月)に「東京」に名称変更されたものである。1869年3月28日に、日本の国の政治の中枢機能が移転され、京都に都としての位置付けを残したまま、「東京」に奠都された。</p>
    <h3>年表</h3>
    <p>1868年(慶応4年)に府制を施行、東京府となった。<br>

1878年(明治11年)に伊豆諸島、1880年(明治13年)に小笠原諸島を編入。<br>

1889年(明治22年)5月1日には、東京15区を東京府から分立して東京市とした。</p>
    
    <h2>行政区分</h2>
    
    <h3>東京府</h3>
    <ul>
      <li>1868年(慶応4年、明治元年)から1943年まで。</li>

    </ul>
    <h3>東京市</h3>
    <ul>
      <li>1889年(明治22年)から1943年(昭和18年)まで。</li>

    </ul>
    <h3>東京都</h3>
    <ul>
      <li>戦時統制の一環として、1943年、東京都制が内務省主導で施行された。これにより、東京府と東京市は廃止され、代わって旧東京府内・旧東京市内の区域を以て、東京都が設置された。</li>

    </ul>
    <h3>東京都区部</h3>
    <ul>
      <li>東京都区部は、東京都東半分を占める23個の特別区から構成される地域である。旧東京市15区(麹町区・神田区・日本橋区・京橋区・芝区・麻布区・赤坂区・四谷区・牛込区・小石川区・本郷区・下谷区・浅草区・本所区・深川区)の各区部は後に大東京35区に再編・拡大され、戦後東京22区を経て現在の東京23区となった。</li>

    </ul>

  </main>
</body>
body {
  display: flex;
  flex-flow: space-between;
  height: 100vh;
  margin: 0;
  color: #333344;
  line-height: 1.53em;
  font-size: 1.2rem;
  background-color: #efeeff;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: 300;
  margin-top: 3rem;
  color: #111;
}

main {
  flex: 3 3 100%;
  overflow: auto;
  padding: 2rem 3rem;
}

nav {
  flex: 1 1 300px;
  background-color: #111;
  min-width: 240px;
  paddings: 1rem;
  overflow-y: auto;
}

h1,
h2,
p {
  max-width: 100%;
}

a {
  color: #fff;
  text-decoration: none;
}

.active {
  color: yellow;
  font-weight: bold;
}

ul {
  list-style: none;
  padding-left: 1.25rem;
}

ul li::before {
  content: "\2022";
  color: #fff;
  font-weight: bold;
  display: inline-block;
  width: 1em;
  margin-left: -1em;
}
let tocId = "toc";

let headings;
let headingIds = [];
let headingIntersectionData = {};
let headerObserver;

function setLinkActive(link) {
  const links = document.querySelectorAll(`#${tocId} a`);
  links.forEach((link) => link.classList.remove("active"));
  if (link) {
    link.classList.add("active");
  }
}

function getProperListSection(heading, previousHeading, currentListElement) {
  let listSection = currentListElement;
  if (previousHeading) {
    if (heading.tagName.slice(-1) > previousHeading.tagName.slice(-1)) {
      let nextSection = document.createElement("ul");
      listSection.appendChild(nextSection);
      return nextSection;
    } else if (heading.tagName.slice(-1) < previousHeading.tagName.slice(-1)) {
      let indentationDiff =
        parseInt(previousHeading.tagName.slice(-1)) -
        parseInt(heading.tagName.slice(-1));
      while (indentationDiff > 0) {
        listSection = listSection.parentElement;
        indentationDiff--;
      }
    }
  }
  return listSection;
}

function setIdFromContent(element, appendedId) {
  if (!element.id) {
    element.id = `${element.innerHTML
      .replace(/:/g, "")
      .trim()
      .toLowerCase()
      .split(" ")
      .join("-")}-${appendedId}`;
  }
}

function addNavigationLinkForHeading(heading, currentSectionList) {
  let listItem = document.createElement("li");
  let anchor = document.createElement("a");
  anchor.innerHTML = heading.innerHTML;
  anchor.id = `${heading.id}-link`;
  anchor.href = `#${heading.id}`;
  anchor.onclick = (e) => {
    setTimeout(() => {
      setLinkActive(anchor);
    });
  };
  listItem.appendChild(anchor);
  currentSectionList.appendChild(listItem);
}

function buildTableOfContentsFromHeadings() {
  const tocElement = document.querySelector(`#${tocId}`);
  const main = document.querySelector("main");
  if (!main) {
    throw Error("A `main` tag section is required to query headings from.");
  }
  headings = main.querySelectorAll("h1, h2, h3, h4, h5, h6");
  let previousHeading;
  let currentSectionList = document.createElement("ul");
  tocElement.appendChild(currentSectionList);

  headings.forEach((heading, index) => {
    currentSectionList = getProperListSection(
      heading,
      previousHeading,
      currentSectionList
    );
    setIdFromContent(heading, index);
    addNavigationLinkForHeading(heading, currentSectionList);

    headingIds.push(heading.id);
    headingIntersectionData[heading.id] = {
      y: 0
    };
    previousHeading = heading;
  });
}

function updateActiveHeadingOnIntersection(entry) {
  const previousY = headingIntersectionData[entry.target.id].y;
  const currentY = entry.boundingClientRect.y;
  const id = `#${entry.target.id}`;
  const link = document.querySelector(id + "-link");
  const index = headingIds.indexOf(entry.target.id);

  if (entry.isIntersecting) {
    if (currentY > previousY && index !== 0) {
      console.log(id + ":1 enter top");
    } else {
      console.log(id + ":2 enter bottom");
      setLinkActive(link);
    }
  } else {
    if (currentY > previousY) {
      console.log(id + ":3 leave bottom");
      const lastLink = document.querySelector(`#${headingIds[index - 1]}-link`);
      setLinkActive(lastLink);
    } else {
      console.log(id + ":4 leave top");
    }
  }

  headingIntersectionData[entry.target.id].y = currentY;
}

function observeHeadings() {
  let options = {
    root: document.querySelector("main"),
    threshold: 0.1
  };
  headerObserver = new IntersectionObserver(
    (entries) => entries.forEach(updateActiveHeadingOnIntersection),
    options
  );
  Array.from(headings)
    .reverse()
    .forEach((heading) => headerObserver.observe(heading));
}

window.addEventListener("load", (event) => {
  buildTableOfContentsFromHeadings();
  if ("IntersectionObserver" in window) {
    observeHeadings();
  }
});

window.addEventListener("unload", (event) => {
  headerObserver.disconnect();
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.