How can I stop a JavaScript function from affecting a child element? - javascript

I have looked around for a solution to this problem, but it's so wonky that I doubt many people have considered it. A web application that I've recently inherited as a web administrator runs on Drupal (version 6). Unfortunately, I am unable to edit the Drupal install or create a custom theme until May due to other obligations that must be tended to first.
First off, I realize and whole-heartedly agree that there are more efficient (and elegant) ways of solving this problem, but for the time being, this is what the client wants and is comfortable with implementing.
Anyways, the old administrator was using Drupal to develop the site's navigation. The code that Drupal spits out on the HTML page is:
<ul class="menu">
<li class="expanded first">Housing
<ul class="menu">
<li class="leaf first">Housing Roster</li>
<li class="leaf">My Floor</li>
<li class="leaf">Photo Viewer</li>
<li class="leaf last">Building Maintenance</li>
</ul>
</li>
<li class="expanded">Reports
<ul class="menu">
<li class="leaf first">Build Floor Report</li>
<li class="leaf">Submit Incident Report</li>
<li class="leaf last">Submit Lockout</li>
</ul>
</li>
<li class="expanded">Office
<ul class="menu">
<li class="leaf first">Check In Package</li>
<li class="leaf">Check Out Building Package</li>
<li class="leaf">Check Out Campus Package</li>
<li class="leaf last">Check Equipment Out</li>
</ul>
</li>
<li class="expanded">Staff
<ul class="menu">
<li class="leaf first">Pre Programs</li>
<li class="leaf">Post Program</li>
<li class="leaf">Programming Database</li>
<li class="leaf last">Staff Downloads</li>
</ul>
</li>
<li class="leaf">My account</li>
<li class="leaf last">Log out</li>
The biggest concern I have with this output is that there is a ul element with the class attribute of "menu" nested inside of another ul element with the class attribute of "menu."
The goal of the JavaScript function that I am writing is to
Allow for jQuery to expand and collapse the child ul element whenever the respective li element with a class of "expanded" is clicked
Change the href attribute of ONLY the li elements with a class of "expanded" to "javascript:void(null)" so that they don't redirect the user to any page
Here is the JavaScript function that I've got going so far:
<script type="text/javascript">
$(function() {
//Hide the nested lists
$('ul.menu li.expanded ul.menu').hide();
//Change the destination of the header links
$('ul.menu li.expanded a').attr("href", "javascript:void(null)");
//Toggle the display of the nested lists
$('ul.menu li.expanded a').click(function() {
$(this).next().slideToggle('normal');
});
});
</script>
This works just fine, except it changes the href attribute of the nested li elements to "javascript:void(null)" as well. Is there a way that I can alter my JavaScript function to make sure that it applies the new href attribute ONLY to the li elements with a class of "expanded" and not to the li elements with a class of "leaf?"
At this point, I'm really only interested in an alteration of my JavaScript function. Like I said, I know and agree that there are better methods (such as altering the html output of the Drupal theme to begin with), but if I can get a quick fix in as a temporary solution while I rebuild the entire application, that would be awesome!
Please let me know if you have any suggestions!!!
THANKS!!!

Use a more specific selector:
$('ul.menu > li.expanded > a').attr( ...
The > matches only immediate children instead of all children.

If you're just trying to prevent the browser from following the href, you don't need the "javascript:void(null)". What you want to do instead is to stop the event from propagating:
$("ul.menu li.expanded > a").click(function(event) {
event.stopPropagation();
...
});

You could try
$('ul.menu li.expanded>ul.menu').hide();
This makes it apply only to the next element.
Another way would be to use
$('ul.menu li.expanded ul.menu:first').hide();

You can use the direct child selector ">".
e.g:
<script type="text/javascript">
$(function() {
//Hide the nested lists
$('ul.menu li.expanded ul.menu').hide();
//Change the destination of the header links
$('ul.menu li.expanded > a').attr("href", "javascript:void(null)");
//Toggle the display of the nested lists
$('ul.menu li.expanded > a').click(function() {
$(this).next().slideToggle('normal');
});
});
</script>

Related

add class to next sibling element In Javascript

How could I add toggle class? When I click on anchor tag it should add class to only next sibling element ( .treeUlChild ). i tried a lot and try to find solution but couldn't. I am new and this is my first project in javascript.
here is my html code.
<div id="treeList" class="treeDiv">
<ul class="treeUl">
<li>
GUIDELINES
<ul class="treeUlChild treeLevel2">
<li> Guidlines 1</li>
<li> Guidlines 2</li>
<li> Guidlines 3</li>
<li> Guidlines 4</li>
</ul>
<!-- End Child Ul -->
</li>
<li>
AFTER-SALES
<ul id="test" class="treeUlChild treeLevel2">
<li>xyz</li>
<li>
def
<ul class="treeUlChild treeLevel3">
<li>
ASSETS
<ul class="treeLevel4">
<li>DIGITAL</li>
<li>OOH</li>
<li>POS</li>
<li>PRINT</li>
<li>SOCIAL GIF</li>
<li>SOCIAL VIDEOS</li>
</ul>
<!-- End Child Ul -->
</li>
</ul>
<!-- End Child Ul -->
</li>
</ul>
<!-- End Child Ul -->
</li>
</ul>
<!-- End treeUl -->
</div>
This is my javascript code.
document.querySelector('#treeList ul li a').addEventListener("click", function(){
document.querySelector('.treeUlChild').nextSibling.classList.toggle('done');
});
One issue is nextSibling returns a node object, it's better you use nextElementSibling which returns an element node. The other issue is querySelector will always return the first element with the specified selector, so the changes will always be reflected on the same element whichever link you clicked. You may rather use querySelectorAll which returns all the elements as a node list, and loop through each element and apply the changes. Another thing is, it's better to use event.target to get clicked element and rather than using a selector again.
document.querySelectorAll('#treeList ul li a').forEach(elem => elem.addEventListener("click", function(){
event.target.nextElementSibling.classList.toggle('done');
}));
There is very simple way using Bootstrap by the way.
But if you want to do that with pure Javascript, you're on the right way to it.
So first, transform your query selector into a object e.g:
var el = document.querySelector('#treeList ul li a');
forEach method, querying the single object clicked in the array of multiple objects:
el.forEach(yourFunctionName());
Add functions to your elements:
<li><a onclick="yourFunctionName()" href="#"> Guidlines 1</a></li>
<li><a onclick="yourFunctionName()" href="#"> Guidlines 2</a></li>
<li><a onclick="yourFunctionName()" href="#"> Guidlines 3</a></li>
<li><a onclick="yourFunctionName()" href="#"> Guidlines 4</a></li>
ps: you can simplify this.
Structure your function:
function myFunctionName(){
document.querySelector('.treeUlChild').nextSibling.classList.toggle('done');
}

Change link title with JS

how can I change the text/name(All items)of the first link using JS without adding id/classes.
<ul>
<li class="current"><a data-filter="*" href="#">All Items</a></li>
<li class="cat-item cat-item-94"> Oslavy</li>
<li class="cat-item cat-item-93">Svadby</li>
</ul>
Thanks
Grab the element with document.querySelector and then modify it's title property directly:
document.querySelector('[data-filter="*"]').title = "Banana";
<ul>
<li class="current"><a data-filter="*" href="#">All Items</a></li>
<li class="cat-item cat-item-94"> Oslavy</li>
<li class="cat-item cat-item-93">Svadby</li>
</ul>
Assuming your link that you want to change will only have its parent "current" as class and link being the first child of the parent you can try following.
window.onload = function()
{
document.getElementsByClassName('current')[0].childNodes[0].innerHTML="Hello";
}
<ul>
<li class="current"><a data-filter="*" href="#">All Items</a></li>
<li class="cat-item cat-item-94"> Oslavy</li>
<li class="cat-item cat-item-93">Svadby</li>
</ul>
Assuming that you are using jQuery, there are several ways to update the first list item, depending how your HTML was built.
If you are sure the item with the current class will always be the first :
$('ul li.current a').html('Banana');
If you prefer to rely on the order of appearance of your list item, just take the first one:
$('ul li:first-child a').html('Apple');
Notice: Be carefull not to use a too generic selector. There may be unwanted changes on your whole page. In your case, it would be better to have a clearly identifiable parent container and use it to target your list safely.

Hiding subsections of a timeline based on current slide?

I have a timeline for my product tour that has 4 main sections, with 4-5 subsections in between, it is set up like this:
<ul class="slideshow-timeline">
<li class="active-target-main main-section">Target
<ul class="current-section target-sub">
<li>Donor Profiles</li>
<li>Segmentation</li>
<li>Custom Lists</li>
<li>RFM Analysis</li>
<li>Wealth Screening</li>
</ul>
</li>
<li class="main-section">Connect
<ul class="current-section connect-sub">
<li>Email Marketing</li>
<li>Social Media</li>
<li>Direct Mail</li>
<li>Welcome Series</li>
</ul>
</li>
<li class="main-section">Convert
<ul class="current-section convert-sub">
<li>Donation Forms</li>
<li>Automated Receipts</li>
<li>Events</li>
<li>Member Mgmt</li>
<li>Moves Mgmt</li>
</ul>
</li>
<li class="main-section">Optimize
<ul class="current-section optimize-sub">
<li>Analytics</li>
<li>Campaigns/Funds/Appeals</li>
<li>A/B Testing</li>
<li>Task Management</li>
</ul>
</li>
</ul>
I am looking for a JS solution to allow me to hide the connect-sub, convert-sub, and optimize-sub while the .active-tour div has a class of target-panel.
I attempted this by using .css but was wondering if there would be a more-elegant solution?
$(function() {
if($('.active-tour').hasClass('target-panel')) {
$(".connect-sub").css("display", "none");
$(".convert-sub").css("display", "none");
$(".optimize-sub").css("display", "none");
}
});
Since the timeline and slides are set as position:absolute and position:fixed it is not sitting in the page flow, so I can't just target it in css.
In theory, this JS should work, but for some reason, it doesn't want to hide the 3 sub sections. No errors are being thrown either. Here is my working page.
I will continue looking into a solution, but if any of you could point me in the right direction, I'd really appreciate it!
Updated to hide all subnavs and show with javascript:
$(function() {
if($('.active-tour').hasClass('target-panel')) {
$(".target-sub").css("display", "block");
}
});
Updated answer:
Use a containing div for both the main content and the footer timeline. Set the class on this outer div using jQuery and use css selectors to show the correct submenu items accordingly.
updated fiddle here
original answer:
Here's a quick and dirty way to do it:
$('.main-section').on('click',function(){
$('.main-section').removeClass('active-tour');
$(this).addClass('active-tour');
});
.current-section{
display:none;
}
li.active-tour .current-section{
display:block;
}
fiddle here

jQuery toggle allow default href of parent element AND parent element should show child elements (toggle)

I would like to open a link for example "1" (1.html) and afterwards it should show (toggle) all child elements of the site 1 (1-1, 1-2,...). I did a research in google and of course also in stackoverflow.
I only found a similar issue of someone who uses wordpress, which it comes nearly to my wish, but I am not a expert like you guys: http://premium.wpmudev.org/forums/topic/jquery-toggle-and-allow-default
<div class="col col-md-3">
<nav id="#mobile-nav" class="nav clearfix" role="navigation">
<ul class="nav" id="menu-flag-menu">
<li>Startseite</li>
<li>1
<ul>
<li>1-1</li>
<li>1-2</li>
<li>1-3</li>
</ul>
</li>
<li>2
<ul>
<li>2-1</li>
<li>2-2</li>
<li>2-3</li>
<li>2-4</li>
</ul>
</li>
<li>3
<ul>
<li>3.1</li>
<li>3.2</li>
</ul>
</li>
</ul>
</nav>
My jQuery toggle function:
$("#menu-flag-menu li a").click(function(){
// close all opened sub menus
$("#menu-flag-menu > li > ul > li > ul > li > ul").slideUp();
// http://stackoverflow.com/questions/2534162/simple-jquery-toggle-of-children
var ul = $(this).next("ul");
if (ul.is(":hidden")) {
ul.slideDown();
}
else {
ul.slideUp();
} });
My CSS:
ul#menu-flag-menu > li > ul { display:none; }
I hope you can help me, thanks! This is my first question, I don't double post and I try to used my brain, google and stackoverflow, before I ask. :)
I guess the JSfiddle would not help for imagine my demand, but here it is:
http://jsfiddle.net/tFh9w/1/
http://jsfiddle.net/tFh9w/8/
Because you are using static HTML files, unless you want to make the menu different in each file (I hope you're at least including the menu using SSI), you need JS to detect what page the browser has opened, and to then trigger the opening of the relevant menu item.
First of all let's make it easy and add a level1 class to the level 1 menu elements.
<li class="level1">1
<ul>
<li>1-1</li>
<li>1-2</li>
<li>1-3</li>
</ul>
</li>
<li class="level1">2
etc etc
Then we find the last part of the URL, and use jquery to find the anchor who's href matches it. Then we check if it's a parent or child (only works for two levels, but can be adapted for more) and we open the correct menu item.
$(function(){
var hrefs = $.trim(window.location.href).replace(/\/$/, "").split('/');
var href = hrefs[hrefs.length -1];
href = '1.html'; // we have to cheat in jsfiddle. remove this line in production
// href = '1-1.html';
$('#menu-flag-menu a[href="' + href + '"]').each(function(){
// what are we opening - the child of this link or the parent?
if ($(this).parent().hasClass('level1')) $ul = $(this).parent().children('ul');
else $ul = $(this).closest('ul');
$ul.slideDown();
});
});
In the example I've had to cheat because in jsfiddle the href is always going to be _display

Cufon (jQuery?) selector not to select children

I have a very basic and usual list menu with a submenu:
<ul>
<li>Home</li>
<li>
About
<ul id="sub-menu">
<li>Child1</li>
<li>Child2</li>
</ul>
</li>
</ul>
And then I have a Cufon selector applying a font to the menu:
Cufon.replace('ul li a');
Is there any way to select only the first level of the menu and disregard the other? Right now both levels get the font, but I would like to use something else for submenu.
I am very much a beginner with Javascript, Cufon and jQuery, I tried using child selectors but I had no luck with that. How can I achieve this?
It appears that your JavaScript library uses CSS-style selectors; you can just use
<ul class='containingMenu'>
<li>Home</li>
<li>
About
<ul id="sub-menu">
<li>Child1</li>
<li>Child2</li>
</ul>
</li>
</ul>
with this selector
ul.containingMenu > li > a
where the > causes the selector to apply only to a's that are direct children of li's that are direct children of the ul.containingMenu.
↪ See this example in action at JSFiddle.

Categories