ddtcorex

29 Aug 2026 · 3 min read

Case study: the search page overwrote its own title

A no-results handler ran on every search, so pages with results still showed the 'no results' title, meta, and breadcrumb.

Field notes — a real production bug, project name withheld. Symptom → investigation → fix → lesson.

The symptom

On the search results page, the browser tab title, the meta[name="title"], the <h1>, and the breadcrumb sometimes showed the no-results wording — "no results for …" — even when the page clearly listed results. It was inconsistent: a query with hits could still render as if it had none, which hurt both SEO and the customer's sense of whether their search worked.

Investigation

The template had two branches: a "no results" block and a "results" block. The no-results branch carried a <script> that rewrote the page's title and headings; the results branch did not. But the script was not nested inside the no-results branch — it sat after both, wrapped only in a DOM-ready listener with no condition guarding whether results actually existed:

<script>
    document.addEventListener('DOMContentLoaded', function () {
        const noResultsText = <?= json_encode((string)$noResultsText) ?>;
        const metaTitle = document.querySelector('meta[name="title"]');
        const title = document.querySelector('title');
        const pageTitle = document.querySelector('h1.page-title span');
        const breadcrumb = document.querySelector('.breadcrumbs li.search span:last-child');

        if (metaTitle) metaTitle.setAttribute('content', noResultsText);
        if (title) title.textContent = noResultsText;
        if (pageTitle) pageTitle.textContent = noResultsText;
        if (breadcrumb) breadcrumb.textContent = noResultsText;
    });
</script>

The variable $noResultsText was always present (it just happened to be empty when there were results), so json_encode always produced a string and the listener always ran. On every search page — hits or not — the script overwrote the title with that string. When the string was empty, the title went blank; when it carried the wording, the page falsely claimed no results.

Root cause

A DOM-mutation script with no guard on the actual condition. The markup branched correctly, but the JavaScript ignored the branch and executed unconditionally on DOMContentLoaded. The bug is a classic one: the server knew whether there were results, and even computed $noResultsText, but never used that fact to decide whether to emit the script at all.

The fix

Gate the entire script behind the server-side condition. If there are no results, emit the script (and register it for CSP); otherwise, emit nothing:

<?php if ($noResultsText): ?>
    <script>
        document.addEventListener('DOMContentLoaded', function () {
            const noResultsText = <?= json_encode((string)$noResultsText) ?>;
            const metaTitle = document.querySelector('meta[name="title"]');
            const title = document.querySelector('title');
            const pageTitle = document.querySelector('h1.page-title span');
            const breadcrumb = document.querySelector('.breadcrumbs li.search span:last-child');

            if (metaTitle) metaTitle.setAttribute('content', noResultsText);
            if (title) title.textContent = noResultsText;
            if (pageTitle) pageTitle.textContent = noResultsText;
            if (breadcrumb) breadcrumb.textContent = noResultsText;
        });
    </script>
    <?php isset($hyvaCsp) && $hyvaCsp->registerInlineScript() ?>
<?php endif; ?>

Now the rewriter exists only on a genuine no-results page, and pages with hits keep their real title, meta, and heading.

Lesson

Never run a DOM-mutation script unconditionally when the server already knows the state it depends on. If a piece of JavaScript should only act in one branch, emit it only in that branch — don't rely on a runtime check that the browser can't know any better than the template can. The cheapest guard is the one you write in PHP: <?php if ($condition): ?> … `. It also keeps CSP registration tied to actual script emission, so you're not whitelisting inline scripts on pages that never use them.

magento2hyvafrontendsearch