Duplicate Drupal dhtml-menu items to use again

I have some sub menus that I want to use as the dominant secondary nav when the user lands on the sub pages.

Now I’ve yet to be able to figure out how to reuse a menu in more that one place on the page. Maybe I need to do a more extensive module search but I set out to to attempt it with jQuery and would you believe it… It worked!

The challenges:
Finding the menu item to copy: Drupal’s dhtml-menu only uses id tags on the a tag of the active link that’s hovered on. That a# tag is not associated with the parents or children of the active menu item. Additionally the id tags are dynamically generated, so it’s a number not a name; ex: a#dhtml_menu-356.

Keep in mind that all of these examples have javascript: in front of them that’s because I tested these as bookmarklets. Essentially just pasting them in to the url bar.
Being a newbie to jQuery I couldn’t recall if there was a way to find elements in a page by the innerHTML or value. Oh boy was I excited when I found :contains(). I can get a collection of all links that contain the word job:

$("a:contains('Job')") or better yet hone in on the specific menu item I want: $("a:contains('Job Applicants')")

.

And we seemingly have the winner!

javascript:var bob=jQuery.extend(true, {}, $("a:contains('Find It Fast')").parent()); alert(bob.insertBefore($("h1.title")));

BUT it only moves the fully functioning menu item. I want a copy of it. So combine this with clone(true):

javascript:var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(true)); alert(bob.insertBefore($("a:contains('Find It Fast')").parent()));

And we seemingly have a champion!

But alas it works inside the same parent maintaining the sub menu items for both the original and the clone but if I insert the clone into a different element then it keeps the sub menu items but the original does not…

cleaner chained version:

javascript:; $("h1.title").before("<div><ul></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(true)).appendTo($("#subnav_menu")); alert(bob);

That works great. And now perhaps the thing to do is replace the div ID’s with new ones so clicking a link on the left wont hide a link in the main nav…

ex from http://stackoverflow.com/questions/416081/jquery-clone-duplicate-ids:

$("#MainConfig").clone(false).find("*").removeAttr("id").appendTo($("#smallConfig"));
javascript:; $("h1.title").before("<div><ul></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(false).find("*").removeAttr("id")).appendTo($("#subnav_menu")); alert(bob);

OK That worked! but maybe renaming is better than removing?

javascript:; $("h1.title").before("<div><ul></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(false).id.replace('#dhtml_menu', '#dhtml_menu_sub')).appendTo($("#subnav_menu")); alert(bob);

Not quite…

how about a function (http://old.nabble.com/Rename-several-divs-td11851468s27240.html):

$('div[@id^="foo-"]').attr('id', function() {
     return this.id.replace('foo', 'bar');
});
$('#subnav_menu a[@id^="dhtml_menu"]').attr('id', function() { return this.id.replace('dhtml_menu', 'dhtml_menu_sub');});
javascript:; $("h1.title").before("<div><ul></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(true)).appendTo($("#subnav_menu"));  $('#subnav_menu a[@id^="dhtml_menu"]').attr('id', function() { return this.id.replace('dhtml_menu', 'dhtml_menu_sub');});
 alert(bob);

That does it all! Oh yeah!!!!!

Oh well it stopped working…. now it’s not getting the submenu… I must have altered something.

javascript:; $("h1.title").before("<div class='tabs'><ul class='tabs primary'></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(false).find("*").attr('id', function() { return this.id.replace('dhtml_menu', 'dhtml_menu_sub');})).appendTo($("#subnav_menu")); alert(bob);

Well that one seemed to work! but without correct syntax in the links…
So maybe true again?

javascript:; $("h1.title").before("<div class='tabs'><ul class='tabs primary'></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(true).find("*").attr('id', function() { return this.id.replace('dhtml_menu', 'dhtml_menu_sub');})).appendTo($("#subnav_menu")); alert(bob);

True is still un-nesting the li and a’s.
Back up:

javascript:; $("h1.title").before("<div class='tabs'><ul class='tabs primary'></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").parent().clone(true)).appendTo($("#subnav_menu")); alert(bob);

Well the nesting is right but the ID tags still need to altered… Problem: only the top level is shown

So lets try to get the child:

javascript:; $("h1.title").before("<div class='tabs'><ul class='tabs primary'></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants')").child().clone(true)).appendTo($("#subnav_menu")); alert(bob);

Oh my goodness this nearly worked:

javascript:; $("h1.title").before("<div class='tabs'><ul class='tabs primary'></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants') + ul").parent().clone(true)).appendTo($("#subnav_menu")); alert(bob);

Remove the parent and bingo!

javascript:; $("h1.title").before("<div class='tabs'><ul class='tabs primary'></ul></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants') + ul").clone(true)).appendTo($("#subnav_menu")); alert(bob);

Here’s the output:

<div class="tabs"><ul class="tabs primary"><ul class="menu"><li class="leaf first dhtml-menu "><a href="/hr2/node" title="" id="dhtml_menu-355">Overview</a></li>
<li class="leaf  dhtml-menu "><a href="/hr2/node" title="" id="dhtml_menu-356">Job Opportunities</a></li>
<li class="leaf  dhtml-menu "><a href="/hr2/node" title="" id="dhtml_menu-357">How to Apply</a></li>

<li class="leaf  dhtml-menu "><a href="/hr2/node" title="" id="dhtml_menu-358">Check Job Status</a></li>
<li class="leaf  dhtml-menu "><a href="/hr2/node" title="" id="dhtml_menu-359">Job Application</a></li>
<li class="leaf last dhtml-menu "><a href="/hr2/node" title="" id="dhtml_menu-360">FAQ</a></li>
</ul></ul></div>

So it would be better to appendTo(‘#subnav’) and add 2 classes to the first ul element: “tabs primary”: .appendTo($(“#subnav”))

.appendTo($("#subnav"))
.addClass("tabs primary")
javascript:; $("h1.title").before("<div class='tabs'></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants') + ul").clone(true)).appendTo($("#subnav")); $("#subnav &gt; ul").addClass("tabs primary"); alert(bob);

Yahoo!

Now let’s clean up the output with .removeClass()

javascript:; $("h1.title").before("<div class='tabs'></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants') + ul").clone(true)).appendTo($("#subnav")); $("#subnav &gt; ul").addClass("tabs primary"); $("#subnav &gt; ul li").removeClass(); alert(bob);

Great… now back to renaming the id tags or removing them

javascript:$("h1.title").before("<div class='tabs'></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants') + ul").clone(true)).appendTo($("#subnav")); $("#subnav &gt; ul").addClass("tabs primary").removeClass("menu"); $("#subnav &gt; ul li").removeClass();  $("#subnav a").removeAttr("id"); alert(bob);

So now the goal would be to achieve the same result but by chaining the commands together so we don’t make so many $() calls…

Well here’s one less: I combined the removeClass and removeAttr… I also changed alert to void

javascript:$("h1.title").before("<div class='tabs'></div>"); var bob=jQuery.extend(true, {}, $("a:contains('Job Applicants') + ul").clone(true)).appendTo($("#subnav")); $("#subnav &gt; ul").addClass("tabs primary").removeClass("menu"); $("#subnav &gt; ul li,#subnav a").removeClass().removeAttr("id"); void(bob);
Leave a Comment