_content: add anchor links to article headers

These anchor links allow users to copy the links for jumping tos
the specific sections easily.

We use javascript to add these links to handle both .html
and .md file format contents. If we had only markdown-based
contents, we could've adjusted the markdown-to-html renderer
instead of relying on javascript. Alternatively, we can try
to rewrite the html in a later pass, but html dom query and
rewrite is simpler in JS.

In order to minimize styling changes in the header text,
we set up the anchor on ¶ symbol that appears only on hover.
This is similar to how pkg.go.dev handles header anchor links.

Adjust the code for TOC generation (in godocs.js), since
now the heading elements include ¶, we want to drop it but
use the first part of the heading elements when compiling
TOC.

Fixes golang/go#68596

Change-Id: Idfe4734c54c636e426b579fdcd697156e87b5747
Reviewed-on: https://go-review.googlesource.com/c/website/+/601055
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/_content/css/styles.css b/_content/css/styles.css
index 11dfb5d..c638e7a 100644
--- a/_content/css/styles.css
+++ b/_content/css/styles.css
@@ -871,6 +871,13 @@
 .Article img {
   max-width: 100%;
 }
+.Article a.Article-idLink {
+  opacity: 0;
+}
+.Article a.Article-idLink:hover {
+  opacity: 1;
+  padding: 0.2rem;
+}
 .CopyPaste {
   display: flex;
   align-items: center;
diff --git a/_content/js/godocs.js b/_content/js/godocs.js
index e6e7d48..088c03a 100644
--- a/_content/js/godocs.js
+++ b/_content/js/godocs.js
@@ -44,9 +44,12 @@
       .each(function() {
         var node = this;
         if (node.id == '') node.id = 'tmp_' + toc_items.length;
+        // header may contain other elements so we use only the first element text.
+        // e.g. <h2>Title <a href="#title">¶</a></h2>
+        const text = node.firstChild.textContent;
         var link = $('<a/>')
           .attr('href', '#' + node.id)
-          .text($(node).text());
+          .text(text);
         var item;
         if ($(node).is('h2')) {
           item = $('<dt/>');
diff --git a/_content/js/site.js b/_content/js/site.js
index 09ac6f3..16d1edd 100644
--- a/_content/js/site.js
+++ b/_content/js/site.js
@@ -54,7 +54,7 @@
         e.target.classList.add('forced-closed');
         e.target.classList.remove('forced-open');
       });
-      
+
       // ensure focus is removed when esc is pressed
       const focusOutOnEsc = e => {
         if (e.key === 'Escape') {
@@ -351,6 +351,16 @@
     }
   }
 
+  // setAnchors adds anchor links to article headers.
+  function setAnchors() {
+    var headers = document.querySelectorAll('.Article h1[id], .Article h2[id], .Article h3[id], .Article h4[id]')
+    if (headers) {
+      headers.forEach(element => {
+        element.insertAdjacentHTML('beforeend', `<a href="#${element.id}" class="Article-idLink" aria-label="Go to ${element.id}">¶</a>`);
+      });
+    }
+  }
+
   initialThemeSetup();
 
   const onPageLoad = () => {
@@ -358,6 +368,7 @@
     setDownloadLinks();
     setThemeButtons();
     setVersionSpans();
+    setAnchors();
     registerPortToggles();
     registerCookieNotice();
   };