Line-Based Read More/Read Less in Vanilla JavaScript – ReadMore Lines
You can use it to create compact article excerpts, product descriptions, comment previews, and card layouts on small screens.
<button> toggle with aria-expanded, aria-controls, and keyboard activation via Enter and Space.1. Install the package from npm or yarn and import the default export in your module:.
# Yarn $ yarn add readmore-lines # NPM $ npm install readmore-lines
import readmore from 'readmore-lines';
2. For a simple browser page, load the minified UMD build from a CDN and call the global readmore() function.
<script src="https://unpkg.com/readmore-lines@latest/dist/readmore.min.js"></script>
3. Truncate a single block of text to a fixed line count. The library counts the element’s rendered lines before doing anything else. If the count is at or below `linesLimit`, the function exits early and no button is injected. You get no empty toggle buttons on short content.
<div id="article-excerpt">
<p>
The northern coast trail runs through three separate wildlife reserves before
reaching the cliffside viewpoint. The full route covers approximately 18km
and takes between four and six hours depending on pace. Spring is the best
season for the trail. Wildflowers cover the hillsides in April and May, and
the coastal fog usually clears by mid-morning. Summer brings dry, hot
conditions. Autumn is quieter but the trail can become muddy after rain.
The final descent to the viewpoint is steep and requires appropriate footwear.
</p>
</div> readmore({
targetElement: document.getElementById('article-excerpt'),
linesLimit: 3 // show 3 lines, hide the rest
}); 4. This is an advanced example for Multiple Cards. Use this setup for article lists, product cards, directory entries, and other repeated content blocks.
<article class="resource-card">
<h3>Admin Dashboard Template</h3>
<p data-expandable-summary>
This dashboard template includes analytics panels, navigation groups,
searchable tables, chart placeholders, and settings screens for SaaS
projects that need a compact preview inside a grid layout.
</p>
</article> summaries.forEach((summary) => {
readmore({
targetElement: summary, // Apply the behavior to the current summary.
linesLimit: 4, // Keep each card height predictable.
readMoreLabel: 'Read full summary',
readLessLabel: 'Show short summary',
targetClass: 'summary-collapsed',
linkClass: 'summary-toggle'
});
}); 5. Use custom classes when the generated toggle needs to match your own UI system.
<p class="article-preview" id="tutorial-preview"> The tutorial explains how to create a reusable upload interface with progress feedback, preview states, validation messages, and a final submit action for the selected files. </p>
<style>
.article-preview {
font-size: 1rem;
line-height: 1.6;
}
.preview-toggle {
margin-top: 0.5rem;
border: 0;
border-radius: 999px;
padding: 0.35rem 0.75rem;
font: inherit;
font-weight: 600;
cursor: pointer;
}
.preview-toggle:focus-visible {
outline: 2px solid currentColor;
outline-offset: 3px;
}
</style> readmore({
targetElement: document.getElementById('tutorial-preview'), // Select the text block.
linesLimit: 2, // Keep the preview short.
readMoreLabel: 'Continue reading',
readLessLabel: 'Hide details',
targetClass: 'article-preview-collapsed',
linkClass: 'preview-toggle'
}); 6. Use the cleanup helpers before you replace text in a single page app, filterable list, or AJAX powered content area.
import readmore, {
destroyReadMore,
hasReadMoreInstance,
getReadMoreInstance
} from 'readmore-lines';
function replacePreviewText(element, nextText) {
const currentInstance = getReadMoreInstance(element);
const currentLimit = currentInstance ? currentInstance.config.linesLimit : 5;
if (hasReadMoreInstance(element)) {
destroyReadMore(element); // Remove the old button, classes, and listeners.
}
element.textContent = nextText; // Replace the visible content safely.
readmore({
targetElement: element, // Reapply the behavior to the updated element.
linesLimit: currentLimit,
readMoreLabel: 'Open full preview',
readLessLabel: 'Close preview'
});
} 7. All available options:
targetElement (HTMLElement, required): The DOM element to truncate. The element must have a parent node in the DOM.linesLimit (number, default: 8): Maximum number of visible lines before truncation kicks in. Must be a positive integer.readMoreLabel (string, default: 'Read more...'): Label text on the toggle button when the content is collapsed.readLessLabel (string, default: 'Read less'): Label text on the toggle button when the content is expanded.targetClass (string, default: 'read-more-target'): CSS class applied to the target element to control truncation. The library adds and removes this class on toggle.linkClass (string, default: 'read-more-link'): CSS class applied to the injected toggle button.8. Instance Management API:
import readmore, {
hasReadMoreInstance,
getReadMoreInstance,
destroyReadMore,
clearReadMoreCache,
invalidateLineHeightCache,
isStyleCached,
isLineHeightCached
} from 'readmore-lines';
// Check whether an element has an active read-more instance
const active = hasReadMoreInstance(document.getElementById('my-content'));
// Retrieve the instance object (includes config, button reference, and state)
const instance = getReadMoreInstance(document.getElementById('my-content'));
if (instance) {
console.log(instance.config.linesLimit); // logs the current line limit
}
// Destroy a specific instance and remove its button from the DOM
destroyReadMore(document.getElementById('my-content'));
// Clear all injected CSS from the document head and reset the style cache
// Use this for a full page reset, such as a framework unmount
clearReadMoreCache();
// Force recalculation of line height for an element
// Call this after changing font-size or line-height on the target element
invalidateLineHeightCache(document.getElementById('my-content'));
// Check the internal caches (useful for debugging)
console.log(isStyleCached('readmore-lines-styles-read-more-target-4')); // true/false
console.log(isLineHeightCached(document.getElementById('my-content'))); // true/false 9. The library injects scoped styles under a [data-readmore-lines-scope] attribute on the parent element. Override any of these custom properties on that ancestor to restyle the toggle button:
/* Target the auto-added scope attribute on the container's parent */[data-readmore-lines-scope] {
--readmore-link-color: #c0392b;
--readmore-link-hover-color: #922b21;
--readmore-link-bg: transparent;
--readmore-link-hover-bg: rgba(192, 57, 43, 0.08);
--readmore-link-padding-y: 0.3rem;
--readmore-link-padding-x: 0.6rem;
--readmore-link-radius: 3px;
--readmore-link-font-weight: 700;
--readmore-focus-ring: 2px solid rgba(192, 57, 43, 0.35);
--readmore-transition: color 0.15s ease, background-color 0.15s ease;
} Q: The toggle button appears but the text is not clamped. What went wrong?
A: The -webkit-line-clamp rule requires display: -webkit-box and -webkit-box-orient: vertical on the same element. The library injects these automatically. If another stylesheet overrides display on the target element after the library runs, the clamp will not work. Check your existing CSS for conflicting display rules on the same selector.
Q: Can I use this in a React or Vue component?
A: Yes. Call readmore() after the component mounts and the element is in the DOM. In React, run it inside useEffect with an empty dependency array. In Vue, use mounted() or onMounted(). Call destroyReadMore() in the cleanup function (useEffect return or beforeUnmount()) to remove the button and event listeners before the component unmounts.
Q: Does it work with IE 11?
A: The library supports IE 9 and above with a WeakMap polyfill. The WeakMap constructor is used for instance tracking and line height caching. Load a polyfill such as core-js/stable before the library script in your IE target builds.
Q: I update the element’s content dynamically. The read-more toggle disappears. How do I handle this?
A: Call destroyReadMore(element) before you update innerHTML. After writing the new content, call readmore() again with the same options. The destroy step removes the old button from the DOM and clears the cached instance. If you skip the destroy step, the next readmore() call will log a warning and exit.
Q: How do I prevent the library from running on elements where the content is already short?
A: You do not need to check this manually. The library counts the element’s rendered lines at initialization and exits early if the count is below linesLimit.
The post Line-Based Read More/Read Less in Vanilla JavaScript – ReadMore Lines appeared first on CSS Script.
Cybercriminals are increasingly weaponizing the trust placed in corporate communication platforms to launch devastating internal…
LANSING, MI. (WOWO) Teen tobacco use in Michigan is rising again, driven largely by e-cigarettes,…
A newly disclosed security vulnerability in Microsoft Teams could allow attackers to spoof local devices,…
WAYNE COUNTY, IND. (WOWO) Western Wayne School District Superintendent Kelly Plank has been placed on…
WAYNE COUNTY, IND. (WOWO) Western Wayne School District Superintendent Kelly Plank has been placed on…
ELKHART, IND. (WOWO) The trial of an Elkhart doctor accused of sexually assaulting a nursing…
This website uses cookies.