12

Get ready for a weird one. So I'm customizing the primary catalog navigation to build a state-full UI behavior that handles multiple interaction models (menus, dropdowns, modals, etc) across devices. Like you do.

That means overriding this class/method:

app/code/core/Mage/Page/Block/Html/Topmenu.php :: _getHtml()

To produce HTML output like this (somewhat simplified):

<ul class="nav-list">
    <li class="nav-1">
        <a data-ui-action="nav-1" href="#">Bazzow</a>
        <div class="menu"> ... </div>
    </li>

    <li class="nav-2">
        <a data-ui-action="nav-2" href="#">Bazinga</a>
        <div class="menu"> ... </div>
    </li>
</ul>

Now, this is pretty boring/standard except for the data-ui-action attribute. That's where the JS magic happens. Any clicks on elements with that attribute update the UI state. You guessed it, the li.nav-X class (which Magento adds) acts as my hook to bind the UI state to the activated element.

All good, right? Turn on EE cache. All good right? Wrong.

If the page you're viewing is within the catalog hierarchy of Bazinga (aka nav-2), then suddenly you will see this:

data-ui-action="nav-2 active"

Who added the nasty active string? The phantom is who.

And now your UI state fails, because the data attribute's value doesn't match the <li> class anymore. Hunt down the phantom.

The Hunt

  1. First, you check that under EE cache the variable $child->getPositionClass() which outputs nav-2 doesn't actually have other (presumably) class values appended. It does not.

  2. You check that one of Magento's many decorator JS scripts isn't executing on the nav list. It's not.

  3. Maybe it's actually some odd thing in /js/varien/menu.js. But you already excluded those core scripts like you always do.

  4. Maybe it's some crazy inline JS you'd never know a module renders out of the PHP class. Search the page source for active within <script> tags. You find nothing.

  5. Maybe it's some other crazy JS Magento requires but loads externally. You disable JS in the browser, but the phantom lives.

  6. You go back to your Topmenu.php class and remove the data attribute. The problem stops. What the hell.

  7. You wonder if another attribute on the same element isn't properly quote-closed (hey, lots of class appending happens in there). So you swap the order of attributes and remove them in various combination. No dice. If the data attribute is present, so is the phantom.

  8. You wonder what if it's not this PHP class doing the deed? There's a dispatched page_block_html_topmenu_gethtml_after event something else might use to hack over the markup from beyond. Nothing.

  9. What. Is. Happening. Here.

The answer

Explain all that to the backend devs. Everyone act confused. Until...

1 Answer 1

10

Somebody cracks open:

app/code/core/Enterprise/PageCache/Model/Container/Catalognavigation.php
Method: saveCache()
Line 107

You see a nasty little regular expression:

if (preg_match('/(?<=\s|^)nav-.+?(?=\s|$)/', $classValue, $matches)) {
    $categoryUniqueClasses .= ($categoryUniqueClasses ? ' ' : '') . $matches[0];
}

Which is matching some devilry for nav- within this <li>. Just so you remember:

<li class="nav-1">
    <a data-ui-action="nav-1" href="#">Bazzow</a>
    <div class="menu"> ... </div>
</li>

Magento is expecting to only find nav- strings on <li> elements, but your data attribute's value is getting matched and hacked onto. Do not want.

So use a different class and data attribute like i-have-a-child-.

The Phantom

A real man makes his own luck, Billy Zane.

2
  • 1
    "Awesome" problem and explanation! Commented Feb 19, 2015 at 21:18
  • haha wow that's actually pretty crazy... In a model of the full page cache module... upvoted
    – Erfan
    Commented Feb 20, 2015 at 1:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.