Guide: Adding a "Read more" button for a long product description or article in Cotonti CMF. There are two options for a ready-made solution for the task.
Guide: Adding a "Read more" button for a long product description or article in Cotonti CMF. There are two options for a ready-made solution for the task.
📘 Introduction
If your Cotonti store (Market module) has product descriptions ({MARKET_TEXT}) that exceed 750 characters, it’s a good idea to hide part of them behind a “Read more” button. This improves page readability without hurting SEO, because the full text remains present in the HTML.
Below are two implementation variants, both supporting multilingual buttons via Cotonti language files. Choose the one that fits your project structure best.
🌐 Preparing the localization
First, add the required strings to the Market module’s language files. If you use a different module, the path will be similar.
Why can we use {PHP.L.*} directly in the script? Because the script is inside a .tpl file, Cotonti processes it as part of the template and replaces the variables with the correct strings before the page is sent to the browser.
✅ Pros of variant 1
Everything is in one place – easy to find and modify.
No need to edit global theme files.
❌ Cons
The template code becomes bulkier.
Aggressive template caching (e.g., with {SOME_TPL}) may cause minor edge cases.
Variant 2: separate files
This is a cleaner approach: HTML remains in the template, CSS goes into your theme’s stylesheet, and JavaScript into a global script. Language strings are passed via data-* attributes because the JS file isn’t processed by the template engine.
The key difference is that the button now has data-read-more and data-collapse attributes carrying the localized strings – these will be picked up by the external JavaScript.
🎨 CSS styles
Add the following to your theme’s stylesheet, e.g., themes/index36/css/default.css (path may vary):
Notice the collapsed height is different from variant 1 (380px vs 200px). Choose whichever value suits your design best.
⚙️ JavaScript
Add this code to your theme’s main JavaScript file, e.g., themes/index36/js/js.js. It’s best to keep it as a standalone handler:
// "Read more" toggle for product card
document.addEventListener('DOMContentLoaded', function() {
const block = document.getElementById('protected-block');
if (!block) return;
const body = block.querySelector('.market-text-body');
const btn = block.querySelector('#readMoreBtn');
if (!body || !btn) return;
const plainText = body.textContent || body.innerText;
const limit = 750;
if (plainText.length > limit) {
body.classList.add('collapsed');
const fade = document.createElement('div');
fade.className = 'text-fade';
body.appendChild(fade);
btn.classList.remove('d-none');
btn.addEventListener('click', function() {
const isExpanded = body.classList.contains('expanded');
if (isExpanded) {
body.classList.remove('expanded');
body.classList.add('collapsed');
btn.textContent = btn.dataset.readMore; // from data attribute
fade.style.opacity = '1';
} else {
body.classList.remove('collapsed');
body.classList.add('expanded');
btn.textContent = btn.dataset.collapse; // from data attribute
fade.style.opacity = '0';
}
});
}
});
Important! Since the JavaScript is in a separate file, the Cotonti template engine does not process it. We cannot write {PHP.L.market_read_more} there. Instead, the strings are read from the button’s data-read-more and data-collapse attributes – that’s why we added them in the HTML.
✅ Pros of variant 2
Clean separation of concerns: HTML, CSS, and JS are not mixed.
CSS and JS can be cached by the browser independently.
Better suited for larger projects with shared codebases.
❌ Cons
Requires editing multiple files.
You must remember to pass localized strings through data attributes.
🔄 Comparison and recommendations
Criteria
Variant 1 (all in template)
Variant 2 (separate files)
Localization
Direct {PHP.L.*} in script
Via data-* attributes on the button
Styles location
Inside the same .tpl file
In a global CSS file (e.g., default.css)
Script location
Inside the same .tpl file
In a global JS file (e.g., js.js)
Maintainability
Simpler – everything is nearby
Scales better
Performance
Slightly faster (fewer HTTP requests)
More efficient static asset caching
Recommendation: If you rarely change scripts and styles and your template isn’t too bloated, go with Variant 1 – it’s the easiest to implement. For larger projects with active development, Variant 2 is preferable – it keeps the code organized and reusable.
🧪 Testing
After applying the changes, open any product that has a description longer than 750 characters. You should see the text truncated with a fading gradient at the bottom and a “Read more” button. Clicking it expands the full text and changes the button to “Collapse”. Clicking again collapses the description. Products with short descriptions will not show the button.
Switch the site language to verify that the button label updates accordingly.
Now you can pick the approach that fits your workflow and integrate it. Both variants work perfectly and don’t harm SEO because the complete text is already present in the HTML source.
Оказываю весь спектр услуг по CMF Cotonti. Разработка открытых и закрытых корпоративных интернет порталов, небольших социальных сетей, торговые площадки, маркетплейсы, биржи фриланса, каталоги товаров оптовых поставщиков, интернет-магазин под заказ, чтобы делать совместные покупки и групповые совместные продажи от имени нескольких продавцов.