Forty-nine “Copy Prompt” buttons vanished from a live AIInsider.in page in September 2026 — not broken, not mis-styled, just gone from the rendered HTML entirely, despite looking perfectly correct in the WordPress editor. Tracking it down took three separate, unrelated bugs stacked on top of each other, and exposed two WordPress-specific traps that will bite anyone who ships interactive JavaScript inside post/page content: wptexturize() corrupting inline attribute quotes, and LiteSpeed Cache silently stripping inline <script> blocks. This piece is the honest, step-by-step account of how we found both — and the fixes, so you don’t have to lose an afternoon to the same thing.
By Shekhar Chandran | Published September 12, 2026 | 8 min read | Bug diagnosed and fixed live on AIInsider.in’s AI Prompts Library page, all details below captured firsthand during the fix.
📅 September 12, 2026 · 🕐 8 min read · 🗂️ Blog

Photo by Daniil Komov on Pexels
This page covers: why 49 clickable buttons disappeared from a live WordPress page while rendering fine in the editor, the three-layer diagnostic method (database → served HTML → parsed DOM) that isolated the cause, how WordPress’s
wptexturize()content filter corrupted an inlineonclickattribute, and how LiteSpeed Cache’s JS optimizer separately stripped an inline<script>block.
This page does not cover: LiteSpeed Cache setup or general WordPress performance tuning from scratch — see LiteSpeed’s own documentation for that, or our AI Prompts Library for the actual feature this bug affected.
Jump to a section:
- The setup: a feature that worked in preview, broke in production
- Symptom #1: things were also just centered wrong
- Symptom #2: the buttons weren’t styled wrong — they didn’t exist
- Root cause #1: wptexturize() corrupting HTML attributes
- The fix: kill inline onclick, use event delegation
- Root cause #2: LiteSpeed Cache stripping the fix itself
- Takeaways for anyone shipping JS inside WordPress content
- AIInsider Verdict
- Quick Answers
The setup: a feature that worked in preview, broke in production
We shipped a small feature on our AI Prompts Library page: 49 “📋 Copy Prompt” buttons, one under each prompt card, each with an inline onclick handler that copies the prompt text to the clipboard. It worked perfectly in the editor preview. We published. Every single button vanished — not broken, gone. Not in the DOM. Not in “view source.” Just not there.
Symptom #1: things were also just… centered wrong
Before we even got to the missing buttons, screenshots showed paragraphs and prompt boxes that should have been left-aligned rendering centered or oddly indented on certain line wraps. That part was simple: several wrapper divs and heading/paragraph styles had no explicit text-align, and the theme’s CSS cascade was centering them under specific conditions. The fix was seven rounds of adding explicit text-align:left inline styles. Not interesting on its own — but it’s worth naming because it almost made us stop looking. We fixed the “obvious” visual bug and nearly moved on, instead of noticing the buttons were missing entirely.
Symptom #2: the buttons weren’t styled wrong — they didn’t exist
This is the one that took real digging. The working theory list, roughly in order of how wrong each one turned out to be:
- Cache serving stale content — ruled out with a forced no-cache fetch, same result
display:nonesomewhere in CSS — ruled out; the elements weren’t in the computed DOM at all, so there was no style to inspect- A plugin stripping
<button>tags — seemed unlikely, worth checking anyway
The technique that actually cracked it was comparing the same content at three different layers:
| Layer | What we checked | Result |
|---|---|---|
| 1. Database | Raw stored post content via the WordPress content API | Clean — buttons present, onclick attribute intact |
| 2. Served HTTP response | Fetched the live URL directly, bypassing any client-side rendering | Corrupted — attribute value cut off mid-string |
| 3. Parsed DOM | document.querySelectorAll('button') in the browser console | Buttons missing entirely — swallowed by the parser |
Layer 1 was fine. Layer 2 was already corrupted. That narrowed the search to something running between “database” and “the HTTP response” — a server-side content filter at render time, not anything client-side.
Root cause #1: WordPress’s own wptexturize() was corrupting our HTML attributes
WordPress runs wptexturize() on the_content at render time — the filter that turns straight quotes into “smart” curly quotes for readable prose. The problem: it doesn’t distinguish between quote characters in your visible text and quote characters inside an HTML attribute value.
Our button markup looked like this:
<button onclick="navigator.clipboard.writeText('...');this.textContent='✓ Copied';">
📋 Copy Prompt
</button>wptexturize() converted some of the straight ' characters inside that onclick string into curly-quote HTML entities. The served HTML no longer had a real closing quote for the attribute value. Following the HTML5 parsing spec, the browser treated the <button> tag’s attribute as never terminating, and silently swallowed everything after it into that one unclosed tag — until the next literal <button> string in the markup forced it closed. 49 buttons, cascading into each other, all eaten by the parser. That’s the honest reason they weren’t just mis-styled — they never existed as separate elements in the parsed DOM at all.
The fix: kill inline onclick, use event delegation
Never put inline onXXX="..." attributes containing quote characters into WordPress post or page content — wptexturize() can corrupt them at render time with no warning. We replaced all 49 buttons with a plain class and moved the click behavior into one delegated listener:
<button class="copy-btn" data-text="the actual prompt text">
📋 Copy Prompt
</button>
<script data-no-optimize="1" data-cfasync="false">
document.querySelectorAll('.copy-btn').forEach(btn => {
btn.addEventListener('click', () => {
navigator.clipboard.writeText(btn.dataset.text);
btn.textContent = '✓ Copied';
});
});
</script>Root cause #2: LiteSpeed Cache stripping the fix itself
Buttons were back in the DOM. Click handler still didn’t fire. Same three-layer trick: the database had the <script> block, the served HTML didn’t. This time it wasn’t wptexturize — it was LiteSpeed Cache’s JS minify/combine optimizer silently stripping the inline <script> block during minification. Confirmed via response headers: x-litespeed-cache: miss on a forced fresh render (ruling out a stale-cache explanation) while x-litespeed-tag showed an active JS-minify hash — meaning the optimizer was actively processing and dropping the block, not just serving a cached copy of it.
The fix: LiteSpeed Cache respects a documented exclusion convention — add data-no-optimize="1" data-cfasync="false" to any inline <script> you need untouched (already in the snippet above). The script appeared in the served HTML immediately after that, and the click handler worked as expected on a real user click.
My take: the part that actually worried me wasn’t either individual bug — it’s that both failed completely silently. No console error, no 500, no broken-looking output; just quietly less functionality than what was in the database. If you’re only testing in the WordPress editor preview (which doesn’t run
wptexturize()or your live cache config the same way), you will ship this exact failure and not notice until a user tells you the button “doesn’t do anything.”
🇮🇳 For Indian WordPress publishers: LiteSpeed Web Server is the default stack on a lot of budget-friendly Indian hosting (it’s cheaper to license than alternatives, which is why it shows up on shared and VPS plans aimed at smaller Indian sites and blogs). If your host runs LiteSpeed and you’re adding any interactive on-page feature via an inline
<script>block, check for this exact silent failure before assuming your JavaScript is simply buggy.
Takeaways for anyone shipping JS inside WordPress content
- Never use inline
onXXXattributes with quoted string literals in post/page content.wptexturize()runs onthe_contentand can corrupt quote characters inside them without warning — it only cares about “readable prose,” not your JavaScript. - Prefer a
classplus a single delegated event listener over per-element inline handlers. Cleaner, and immune to the above by construction. - Any inline
<script>in post content on a LiteSpeed-cached WordPress site needsdata-no-optimize="1", or the JS minifier can silently strip it — no error, no warning, it’s simply gone from the served page. - When something works in the editor/database but not live, diff three layers: stored content → raw served HTTP response → parsed DOM. Whichever layer first shows the corruption tells you exactly which system is responsible, instead of guessing across the whole stack.
- Fixing the obvious visual bug can make you stop looking too early. The centering issue was real, but it wasn’t the actual story — always check for a second, quieter bug hiding behind the loud one.
AIInsider Verdict
What we liked: both root causes were fixable with small, permanent changes (an exclusion attribute and a markup pattern swap) — no plugin conflicts to manage long-term, no ongoing workaround needed.
What holds it back: neither WordPress nor LiteSpeed Cache surfaces any warning when this happens. There’s no admin notice, no error log entry, nothing — you only find out by noticing a feature quietly doesn’t work.
The concrete recommendation: if you’re adding any interactive JavaScript to WordPress post or page content — a copy button, a toggle, a calculator, anything with an event handler — build it with class + a delegated listener and data-no-optimize="1" from the start, before you ever hit this bug. It costs nothing extra to write it the safe way the first time; it costs an afternoon of three-layer debugging to find out the unsafe way is broken.
Quick Answers
What is wptexturize() in WordPress?
It’s a WordPress content filter that runs on the_content at render time, converting plain straight quotes and dashes into “smart” typographic characters (curly quotes, em dashes) for readability. It applies to the entire rendered content, including text inside HTML attributes, which is what causes this bug.
Why did LiteSpeed Cache remove my inline JavaScript?
LiteSpeed Cache’s JS minify/combine optimization processes inline <script> blocks in post content by default, and can strip a block during that process. Excluding a specific block with data-no-optimize="1" data-cfasync="false" is LiteSpeed’s documented way to prevent this.
Does this affect other caching plugins like WP Rocket or W3 Total Cache?
We haven’t verified this directly on those plugins, so we won’t claim it does — this account is specific to LiteSpeed Cache’s JS optimizer, confirmed via its own response headers. If you’re on a different caching plugin and see a similar silent script-stripping issue, check that plugin’s own JS-optimization exclusion settings first.
How do I check if this is happening on my own site?
Fetch your live page’s HTML directly (e.g. via your browser’s devtools Network tab, or a simple fetch() call) and compare it against what’s stored in your WordPress editor. If an inline <script> or an attribute value is missing or truncated in the served version but present in the editor, you’re looking at a render-time content filter or optimizer, not a client-side bug.
More on AIInsider.in
- AI Prompts Library — the 49-prompt page where this exact bug happened; see the live, fixed Copy Prompt buttons in action.
Published September 12, 2026. All technical findings (response headers, DOM comparisons) were captured firsthand while diagnosing and fixing this bug on AIInsider.in’s own WordPress installation. AIInsider.in is independent and not affiliated with WordPress.org, Automattic, or LiteSpeed Technologies.