Fragment Links and In Page Navigation
Link to a spot on the same page with an id and a hash. The basis of tables of contents, skip links, footnotes and back to top links.
-
HTML Basics
- What is HTML: The Structure Layer of Every Web Page
- HTML Document Structure: DOCTYPE, html, head and body
- Elements, Tags and Attributes: The Vocabulary of HTML
- HTML Comments: Notes That Ship With Your Code
- Block Level and Inline Elements
- Writing and Running Your First HTML Page
- How a Browser Turns Markup Into a Page
- Text and Formatting
- Links and Navigation
- Images and Media
- Lists
- Tables
-
Forms
- Form Structure: form, action and method
- Input Types: Text, Email, Number, Date and the Rest
- Labels: The Most Important Element in a Form
- Checkboxes, Radio Buttons and Grouping
- select, option, optgroup and datalist
- textarea, File Uploads and Hidden Fields
- Buttons: submit, reset and button
- Built In Form Validation
- GET or POST: What Happens When a Form Is Submitted
- Semantic HTML
- HTML5 Features
- Head and Metadata
- HTML with CSS
- HTML with JavaScript
- Accessibility
-
HTML SEO
- How Google Works: Crawling, Indexing and Ranking
- SEO Friendly HTML Structure
- Titles and Descriptions That Earn Clicks
- Headings and Content Structure for Search
- Internal Linking and Anchor Text
- robots.txt and XML Sitemaps
- Canonical URLs and Duplicate Content
- Structured Data and JSON-LD
- Image SEO
- Core Web Vitals and Mobile Friendliness
- DevTools and Debugging
- Editor Productivity
- HTML Best Practices
- HTML Projects
- Advanced Projects
- Practice and Exams
Concept
A fragment link points at a location within a document rather than at another document. The href is a hash followed by the id of the target element, and activating it scrolls that element into view and moves the reading position to it.
The fragment is never sent to the server. The browser fetches the page, then handles the hash itself.
Syntax
<a href="#refunds">Jump to refunds</a>
<h2 id="refunds">Refunds</h2>Two special targets need no id:
<a href="#top">Back to top</a>
<a href="#">Also goes to the top, but avoid this form</a>A bare hash works but adds a history entry and is easily confused with a placeholder link. Prefer #top.
Example: a table of contents
<nav aria-label="On this page">
<h2 id="contents">On this page</h2>
<ol>
<li><a href="#eligibility">Who can apply</a></li>
<li><a href="#documents">Documents required</a></li>
<li><a href="#fees">Fees and payment</a></li>
</ol>
</nav>
<section>
<h2 id="eligibility">Who can apply</h2>
<p>...</p>
</section>
<section>
<h2 id="documents">Documents required</h2>
<p>...</p>
</section>
<section>
<h2 id="fees">Fees and payment</h2>
<p>...</p>
<p><a href="#contents">Back to contents</a></p>
</section>Example: a skip link
The first focusable element on the page, usually hidden until it receives focus. Without it, a keyboard user tabs through the entire navigation on every single page before reaching the content.
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header>
<nav><!-- thirty links --></nav>
</header>
<main id="main-content" tabindex="-1">
<h1>Admission 2026</h1>
</main>
</body>The tabindex="-1" matters. Without it, some browsers scroll to the target but leave keyboard focus back at the link, so the next Tab press returns the reader to the navigation they were trying to escape. A negative tabindex makes the element programmatically focusable without adding it to the tab order.
Example: footnotes
<p>Ridership rose by a fifth.<sup><a href="#fn-1" id="ref-1">1</a></sup></p>
<section>
<h2>Notes</h2>
<ol>
<li id="fn-1">District Transport Review, 2026. <a href="#ref-1">Back to text</a></li>
</ol>
</section>The link goes both ways, so a reader who follows a footnote can return to where they were.
The sticky header problem
A fixed header covers the top of the viewport, so a jump target lands underneath it and the reader sees the wrong heading. The fix is one CSS declaration on the target:
:target,
[id] {
scroll-margin-top: 5rem; /* height of the fixed header */
}
html {
scroll-behavior: smooth; /* optional, animated scrolling */
}Wrap the smooth scrolling in a prefers-reduced-motion query. Animated scrolling triggers nausea for some readers, and the operating system already carries their preference.
Important rules
- The
idmust be unique in the document. A duplicate id means the browser jumps to the first one and ignores the rest. - Ids are case sensitive.
#Feeswill not findid="fees". - An id must not contain spaces.
- The hash never reaches the server, so server side code can never see it.
- Following a fragment link changes the URL, so the reader can copy or bookmark the exact position.
- The
:targetpseudo class matches the currently jumped to element, which is a free way to highlight it.
Common mistakes
- Linking to
#Feeswhen the id isfees, then concluding fragment links are unreliable. - Duplicating an id across several sections.
- Forgetting
tabindex="-1"on a skip link target and leaving keyboard focus behind. - Hiding the skip link with
display: none, which removes it from the tab order entirely. Move it off screen instead and bring it back on focus. - Using
href="#"as a placeholder, so a real fragment link and a dead one look identical. - Sticky header covering every jump target because
scroll-margin-topwas never set.
Best practices
- Put an id on every heading a reader might want to link to. Lower case, hyphenated, derived from the heading text.
- Keep ids stable. A changed id breaks every bookmark and every inbound deep link.
- Give every page a skip link, and make it the first focusable element.
- Set
scroll-margin-topon jump targets as soon as the design has a fixed header. - Respect
prefers-reduced-motionbefore enabling smooth scrolling.
Practice
- Build a page with four sections and a table of contents that links to each, plus a back to contents link at the end of every section.
- Add a skip link that is invisible until focused and jumps to the main content. Confirm the next Tab press lands inside the content.
- Give a page a sticky header, follow a fragment link and observe the heading disappearing behind it. Fix it with
scroll-margin-top. - Style
:targetwith a background colour and describe how it helps a reader who followed a deep link.