0

I have the following lines of HTML that I am needing to use preg_replace or a similar function to add a button element that can be used for JavaScript events.

<li id="" class="menu-item-has-children">
  <a href="https://example.com">Parent Menu Item</a>
  <!-- Add button HTML element here -->
  <ul class="sub-menu">
    <!-- Child menu items -->
  </ul>
</li>

Pattern idea:

$pattern = '/<li id="(.*?)" class="menu-item-has-children"><a href="(.*?)">(.*?)<\/a><ul class="(.*?)">(.*?)<\/ul><\/li>/s';

Only li elements that have the menu-item-has-children class should be used.

I have used the following WordPress filter function to find and replace anchor elements that do not have a href attribute, but have been unable to modify it to successfully add a button element between the anchor elements and sub-menu unordered lists.

add_filter( 'wp_nav_menu_items', 'themename_filter_empty_anchor_items', 10, 2 );
function themename_filter_empty_anchor_items( $items, $args ) {
  if( $args->theme_location == 'want-to-navigation') {
    $pattern = '/<a>(.*?)<\/a>/s';
    $replacement = '<button aria-expanded="false" data-submenu-toggle="">$1<svg role="img" viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><title>Expand submenu navigation icon</title><path d="m432 256c0 17.69-14.33 32.01-32 32.01h-144v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144h-144c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99h144v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144c17.7-.01 32 14.29 32 31.99z"/></svg></button>';
    $items = preg_replace( $pattern, $replacement, $items );
  }
  return $items;
}
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
Mike Hermary
  • 346
  • 7
  • 22
  • _"to add a button element that can be used for JavaScript events"_ - so why not add it via JS in the first place then? The client is building a DOM for you anyway, so you would not need to do an additional round of DOM creation on the server, and it would not need messing around with HTML with regex (generally rather a no-no, https://stackoverflow.com/a/1732454/1427878) – CBroe Sep 20 '22 at 06:46

2 Answers2

0

You could use DOMDocument in conjunction with DOMXPath to load your HTML, find the appropriate a element and then append a button after it:

$html = <<<EOD
<li id="" class="menu-item-has-children">
  <a href="https://example.com">Parent Menu Item</a>
  <!-- Add button HTML element here -->
  <ul class="sub-menu">
    <!-- Child menu items -->
  </ul>
</li>
EOD;

$doc = new DOMDocument();
$doc->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($doc);
foreach ($xpath->query('//li[@class="menu-item-has-children"]/a') as $a) {
    $button = $doc->createElement('button', 'Your button text');
    // modify button as required
    // ...
    // add to document
    $a->parentNode->insertBefore($button, $a->nextSibling);
}
echo $doc->saveHTML();

Output:

<li id="" class="menu-item-has-children">
  <a href="https://example.com">Parent Menu Item</a><button>Your button text</button>
  <!-- Add button HTML element here -->
  <ul class="sub-menu">
    <!-- Child menu items -->
  </ul>
</li>

Demo on 3v4l.org

Nick
  • 138,499
  • 22
  • 57
  • 95
0

I followed CBroe's suggestion and utilized JavaScript to insert the button elements between the a and ul elements.

The code below details what I implemented. A matchMedia method was used to target the code at screens smaller than 1169 pixels.

const mediaQuery = window.matchMedia('(max-width: 1169px)');
if (mediaQuery.matches) {
  let links = document.querySelectorAll("#global-navigation .menu-item-has-children > a");
  links.forEach(function (link) {
    let linkExpander = document.createElement("button");
    linkExpander.setAttribute("aria-expanded", "false");
    linkExpander.setAttribute("data-submenu-toggle", "");
    let svg = `<svg role="img" viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><title>Expand submenu navigation icon</title><path d="m432 256c0 17.69-14.33 32.01-32 32.01h-144v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144h-144c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99h144v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144c17.7-.01 32 14.29 32 31.99z"></path></svg>`;
    linkExpander.insertAdjacentHTML("beforeend", svg);
    link.after(linkExpander);
  });
}
Mike Hermary
  • 346
  • 7
  • 22