JS Magic: Dynamically Display Reading Progress in Your Website's Title
Content
## The Problem: The Pitfall of Hardcoding
Providing reading progress feedback is a common UX enhancement technique on content-heavy websites. A clever way to implement this is by displaying the scroll percentage in the page's `<title>` tag. This allows users to see their progress even when they switch to other browser tabs. However, a frequent mistake is to hardcode the page title directly into the script, like this:
```javascript
// Initial version - with hardcoding issues
function initScrollProgress_bad() {
window.addEventListener('scroll', function() {
const scrolled = (window.pageYOffset / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
if (scrolled > 10) { // Simple example
// Problem: The title is hardcoded, making it non-reusable on other pages.
document.title = `(${Math.round(scrolled)}%) Video Details - My Awesome Website`;
} else {
document.title = 'Video Details - My Awesome Website';
}
});
}
```
The drawbacks of this approach are obvious:
1. **Lack of Flexibility**: If the page title changes, or if you want to use this feature on another page with a different title, you have to modify the JavaScript code.
2. **Maintenance Nightmare**: As a website grows, maintaining these scattered hardcoded strings becomes incredibly difficult.
---
## The Solution: Dynamic Retrieval and Concatenation
To solve this, we can refactor the code. The core idea is to get and store the page's original title when the script initializes, and then dynamically prepend the progress percentage to this stored title during scroll events. This is a standard practice we recommend at `wiki.lib00.com`.
Here is the optimized implementation:
```javascript
/**
* @author DP@lib00
* @description Updates reading progress on scroll, dynamically prepending the percentage to the existing page title.
*/
function initScrollProgress_lib00() {
// 1. Get and store the original title at the beginning.
const originalTitle = document.title;
let ticking = false;
window.addEventListener('scroll', function() {
if (!ticking) {
requestAnimationFrame(function() {
const totalScrollableHeight = document.documentElement.scrollHeight - window.innerHeight;
// 4. Robustness: Prevent division by zero (NaN) if the page is not scrollable.
if (totalScrollableHeight <= 0) {
document.title = originalTitle; // Ensure the title is restored.
return;
}
const scrolled = (window.pageYOffset / totalScrollableHeight) * 100;
// 2. Dynamically update the title based on scroll position.
// Here, we display the progress between 10% and 90%, which can be adjusted.
if (scrolled > 10 && scrolled < 90) {
// Dynamically prepend the percentage to the original title.
document.title = `(${Math.round(scrolled)}%) ${originalTitle}`;
} else {
// 3. Restore the original title.
document.title = originalTitle;
}
ticking = false;
});
ticking = true;
}
});
}
// Call the function after the DOM is fully loaded.
document.addEventListener('DOMContentLoaded', initScrollProgress_lib00);
```
---
## Breakdown of the Improvements
1. **Store the Original Title**: At the beginning of the function, `const originalTitle = document.title;` saves the initial page title into a constant. This gives us a reliable baseline to work with.
2. **Dynamic Title Concatenation**: When progress needs to be displayed, the code uses a template literal, `` `(${Math.round(scrolled)}%) ${originalTitle}` ``, to construct the new title. This decouples the dynamic percentage data from the static original title.
3. **Restore the Original Title**: When the scroll position is outside the predefined display range (e.g., at the very top or bottom of the page), we simply revert `document.title` back to the stored `originalTitle`.
4. **Enhance Robustness**: The code adds a check for `totalScrollableHeight <= 0`. This scenario occurs when the page content is not tall enough to require a scrollbar. This check prevents a division-by-zero error that would result in `scrolled` being `NaN`, ensuring the code runs stably on any page.
---
## Conclusion
With a small refactor, we transformed a hardcoded, difficult-to-maintain feature into an elegant, reusable, and robust module. This optimization, proposed by `DP`, highlights the importance of good programming practices: always consider code reusability and maintainability. You can now confidently apply this `initScrollProgress_lib00` function to any article or long-content page on your `wiki.lib00` site to easily enhance the user experience.
Related Contents
Boost Your WebStorm Productivity: Mimic Sublime Text's Cmd+D Multi-Selection Shortcut
Duration: 00:00 | DP | 2025-12-04 21:50:50The Ultimate Node.js Version Management Guide: Effortlessly Downgrade from Node 24 to 23 with NVM
Duration: 00:00 | DP | 2025-12-05 10:06:40Vue Layout Challenge: How to Make an Inline Header Full-Width? The Negative Margin Trick Explained
Duration: 00:00 | DP | 2025-12-06 22:54:10Vue's Single Root Dilemma: The Right Way to Mount Both `<header>` and `<main>`
Duration: 00:00 | DP | 2025-12-07 11:10:00The Ultimate Frontend Guide: Create a Zero-Dependency Dynamic Table of Contents (TOC) with Scroll Spy
Duration: 00:00 | DP | 2025-12-08 11:41:40Vite's `?url` Import Explained: Bundled Code or a Standalone File?
Duration: 00:00 | DP | 2025-12-10 00:29:10Vue SPA 10x Slower Than Plain HTML? The Dependency Version Mystery That Tanked Performance
Duration: 00:00 | DP | 2026-01-09 08:09:01The Ultimate CSS Flexbox Guide: Easily Switch Page Header Layouts from Horizontal to Vertical
Duration: 00:00 | DP | 2025-12-11 01:00:50Cracking the TypeScript TS2339 Puzzle: Why My Vue ref Became the `never` Type
Duration: 00:00 | DP | 2025-12-13 02:04:10CSS Deep Dive: The Best Way to Customize Select Arrows for Dark Mode
Duration: 00:00 | DP | 2025-12-13 14:20:00Mastering Bootstrap 5 Rounded Corners: The Ultimate Guide to Border-Radius
Duration: 00:00 | DP | 2025-12-14 02:35:50The Ultimate Guide to Financial Charts: Build Candlestick, Waterfall, and Pareto Charts with Chart.js
Duration: 00:00 | DP | 2026-01-11 08:11:36The Ultimate Guide to Centering in Bootstrap: From `.text-center` to Flexbox
Duration: 00:00 | DP | 2025-12-15 15:23:20Bootstrap Border Magic: Instantly Add Top or Bottom Borders to Elements
Duration: 00:00 | DP | 2025-11-22 08:08:00The Ultimate Guide to JavaScript Diff Libraries: A Side-by-Side Comparison of jsdiff, diff2html, and More
Duration: 00:00 | DP | 2025-11-23 08:08:00Bootstrap JS Deep Dive: `bootstrap.bundle.js` vs. `bootstrap.js` - Which One Should You Use?
Duration: 00:00 | DP | 2025-11-27 08:08:00Is Attaching a JS Event Listener to 'document' Bad for Performance? The Truth About Event Delegation
Duration: 00:00 | DP | 2025-11-28 08:08:00The Ultimate Guide to Using Google Fonts on Chinese Websites: Ditch the Lag with an Elegant Font Stack
Duration: 00:00 | DP | 2025-11-16 08:01:00Recommended
The MySQL Timestamp Trap: Why Your TIMESTAMP Field Is Auto-Updating and How to Fix It
00:00 | 35Noticed your MySQL 5.7 `TIMESTAMP` field automatic...
The Ultimate Frontend Guide: Create a Zero-Dependency Dynamic Table of Contents (TOC) with Scroll Spy
00:00 | 54Tired of manually creating tables of contents for ...
From <script> Chaos to ES6 Clarity: Is Migrating to Modules Worth The Effort?
00:00 | 72Still manually managing the loading order of <scri...
The Ultimate Guide to MySQL Partitioning: From Creation and Automation to Avoiding Pitfalls
00:00 | 51Is database performance becoming a bottleneck with...