Scrollspy does not handle all valid HTML5 element ids
Created by: chrisyeh96
Bug Report
Scrollspy stops working when any of the DOM elements it is "spying" on has an id that starts with a number, such as
<div id="3rd">Text</div>
This should be considered a bug because HTML5 has no restrictions on what form an ID can take (official spec).
There are no other restrictions on what form an ID can take; in particular, IDs can consist of just digits, start with a digit, start with an underscore, consist of just punctuation, etc.
Historically, HTML4 did not allow for ids to start with numbers, but it would seem ridiculous to assume only HTML4 content today.
Bug Demo
See this JSBin for the demo. https://jsbin.com/rehematazo/3/edit?html,output. By default, I've commented out the <a>
elements that link to <div>
s whose IDs start with a digit, so Scrollspy works. But once you uncomment out these <a>
tags, then Scrollspy stops working entirely.
I have also copied the HTML code for the demo here:
Code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<style>
.tall { height: 300px; }
.toc-nav { /* <nav> */
position: sticky;
top: 0;
height: min-content;
max-height: 100vh;
overflow-y: auto;
}
.toc-nav a.active {
color: #007bff;
}
</style>
</head>
<body data-bs-spy="scroll" data-bs-target="#nav">
<div class="container"><div class="row">
<main class="col-8">
<h2 id="A">ID A</h2>
<div class="tall">Chunk of text</div>
<h2 id="B">ID B</h2>
<div class="tall">Chunk of text</div>
<h2 id="C">ID C</h2>
<div class="tall">Chunk of text</div>
<h2 id="1">ID 1</h2>
<div class="tall">Chunk of text</div>
<h2 id="2">ID 2</h2>
<div class="tall">Chunk of text</div>
<h2 id="3">ID 3</h2>
<div class="tall">Chunk of text</div>
</main>
<nav class="col-4 toc-nav" id="nav">
<nav class="navbar navbar-light">
<span class="navbar-brand">Contents</span>
<a class="list-group-item list-group-item-action" href="#A">Item A</a>
<a class="list-group-item list-group-item-action" href="#B">Item B</a>
<a class="list-group-item list-group-item-action" href="#C">Item C</a>
<!-- Uncomment the following 3 lines to see the bug:
Once you uncomment, you will notice that Scrollspy stops working.
-->
<!-- <a class="list-group-item list-group-item-action" href="#1">Item 1</a>
<a class="list-group-item list-group-item-action" href="#2">Item 2</a>
<a class="list-group-item list-group-item-action" href="#3">Item 3</a> -->
</nav>
</nav>
</div></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script>
</body>
</html>
Why this happens, and possible fixes
This bug seems to happen because Bootstrap uses document.querySelector()
to identify elements:
However, document.querySelector()
only works for valid CSS selector strings, and CSS selector strings for IDs cannot start with a number (even though HTML IDs can). See this StackOverflow Q&A for more info: https://stackoverflow.com/q/20306204. This causes Bootstrap JS to encounter the following error (copied from Microsoft Edge console):
Uncaught DOMException: Failed to execute 'querySelector' on 'Document': '#3rd' is not a valid selector.
at <anonymous>:1:10
The StackOverflow Q&A proposes several possible fixes. For example, document.getElementById("3rd")
works fine, but only works with ID-based selectors. I am also not sure which fixes fall within Bootstrap's browser compatibility goals.
System Info
OS: Windows 10 20H1 Browser: Microsoft Edge (Chromium) 91.0.864.59 and Firefox 89.0.2 (64-bit) Bootstrap version 5.0.2