Show/Hide with plus minus - javascript

So I've got it to work that it shows/hides the UL's/LI's, but I'm not sure what I'm doing incorrectly where it's not swapping out the +/- signs?
Here's my JS:
$(".top ul li:not(:has(li.current))").find("ul").hide().end() // Hide all other ULs
.click(function (e) {
if (this == e.target) {
$(this).children('ul').slideToggle();
}
$(this).children("li.menu-item-has-children").text(this.toggle ? "-" : "+");
return false;
});
I have a class setup to append the li with a li:before that add the + sign before the li that has the nested ul's. But I'm not sure if I am going about it the right way to swap out the signs.
Here's the fiddle that I made:
http://jsfiddle.net/bc4mg13a/

There you go: http://jsfiddle.net/bc4mg13a/13/
$(".menu-item-has-children").on("click", function(e){
e.stopPropagation();
var clickedLi = $(this);
$("> ul", clickedLi).slideToggle();
clickedLi.toggleClass("current");
});
To start with, your first js line is a has so much redundant stuff.
$(".top ul li:not(:has(li.current))").find("ul").hide().end() // Hide all other ULs
.click
could be:
$(".top ul li:not(.current)").find("ul").hide().end() // Hide all other ULs
.click
On the other hand, i changed your code slightly, simplified your selectors. On each li click, i select direct ul children, and the i slidetoggle + toggle class the 'current' class.
i also switch the plus sign via the current class on css.

Your code feels incredibly verbose. Well, at least your js. Here's a fiddle of your code that I modified a little bit.
Instead of hiding all your menus with js immediately on pageload, I applied a CSS display: none; to the sub-menu class:
.sub-menu {
display: none;
}
The js is cleaned up a bit, and since the click handler is bound to .menu-item-has-children, You're really only clicking on that to reveal the contained UL.
Give it a look. Hope it helps :)

Simply add:
$(this).toggleClass('open');
To this:
if (this == e.target) {
$(this).children('ul').slideToggle();
$(this).toggleClass('open'); // <--
}

you can just add $(this).toggleClass('open'); before you return false but I would strongly look more into what your code is doing. I'm not so sure the line before is doing anything.

Fixed JS:
$(".top ul li:not(:has(li.current))").find("ul").hide().end() // Hide all other ULs
.click(function (e) {
if (this == e.target) {
$(this).children('ul').slideToggle();
$(this).toggleClass('open'); // added
}
return false;
});
Just added "$(this).toggleClass('open');" to use the class you specified in your CSS instead of trying to manipulate the text manually.

you can do it like this and add $(this).toggleClass('open');
http://jsfiddle.net/bc4mg13a/5/

For how you have it set up, I would try...
$(".top ul li:not(:has(li.current))").find("ul").hide().end() // Hide all other ULs
.click(function (e) {
if (this == e.target) {
$(this).toggleClass("open");
$(this).children('ul').slideToggle();
}
return false;
});
Additional:
For formatting, you might want to do something like:
.menu-item-has-children {
&:before {
content:"+ ";
color: $white;
width: 10px;
display:inline-block;
}
}
.open {
&:before {
content:"- ";
color: $white;
width: 10px;
display:inline-block;
}
}

You don't need to use text(this.toggle ? "-" : "+");
Inside the condition, just toggle the class .open that you have already defined in your SCSS/CSS.
Like so -
$(this).toggleClass('open');
JSFiddle

Related

Jquery selector - affecting element inside same DOM element

My CMS generates menus as lists without any id's and classes. For submenus, there are nested lists.
I made jquery script for expanding submenus:
$(function () {
$(".wrapper ul li").click(function () {
if ($(this).has("ul").length) {
$("a", this).removeAttr('href');
$("ul", this).slideToggle();
}
});
});
My problem is that this script reacts to clicking whole li area and I want it to react to clicking link inside li. Of course I just have to add "a" to selector making it ".wrapper ul li a" but what about condition checking if there is ul nested inside li? And slidetoggle selector. How should I change these?
This might work with minimal modification to your original code.
$(function () {
$(".wrapper ul li a").click(function ()
{
this = $(this).parent();
if ($(this).has("ul").length) {
$("a", this).removeAttr('href');
$("ul", this).slideToggle();
}
}
);
});
Test and see if it works. I have not tested it. Cheers

Mouseleave triggering when leaving grandparent div

I'm having a bit of trouble with a dropdown menu that triggers fadeOut as soon as the mouse leaves the grandparent div, I've searched this problem to death and have yet to find an elegant solution. Here is my code : link
var main = function() {
$('nav').mouseenter(function() {
$('ul li ul').fadeIn('400');
});
$('nav ul li').mouseleave(function(){
$('ul li ul').fadeOut('400');
});
}
$(document).ready(main);
DEMO: MY FIDDLE
You need to specify what element(s) you are trying to attach the event to. By adding '>' youre forcing to only attach the event to that element's children. Try this:
var main = function() {
$('nav').mouseenter(function() {
$(this).find('ul').fadeIn('400');
});
$('nav>ul>li').mouseleave(function() {
$(this).find('ul').fadeOut('400');
});
};
FIDDLE
$(this).find('ul').fadeOut('400');
is correct as $('ul>li>ul').fadeOut('400'); Could not target specific (current) li.
Use following hierarchical flow of TAGS
var main = function() {
$('nav').mouseenter(function() {
$('ul li ul').fadeIn('400');
});
$('nav ul li').mouseleave(function() {
$(this).find('ul').fadeOut('400');
});
};

Shows a dropdown element with jQuery

Currently I'm recreating the Office 2013 ribbon in Html, css and javascript.
This is my first approach so don't judge me on css/html/js code.
Currently the ribbon is working with a dropdown but I have an issue.
The dropdown is showed when you click the corresponding icons, but I don't know how to hide it if I click anywhere in the document.
I can probably come up with a solution, but I'm not too sure that it will be a good approach.
Can someone have a look and given me a good solution to accomplish this?
I've created a fiddle here: http://jsfiddle.net/Complexity/mwCCt/
Here's the code to open it:
$("#OfficeUI .ribbon .tabs > ul li[role=tab] .contents .group .icongroup .icon").children().each(function(index) {
if ($(this).hasClass("menu")) {
var element = $(this);
$('<i class="fa fa-sort-asc arrow"></i>').appendTo($(this).prev());
// Add a click event to the element that contains a menu.
$(this).parent().click(function() {
$(element).toggle();
$(element).parent().addClass("active");
});
}
});
Just click the "New items" button on the ribbon (second one) and then the dropdown menu will open.
Thanks in advance.
Kevin
We can take advantage of the stopPropagation() function:
$("#OfficeUI .ribbon .tabs > ul li[role=tab] .contents .group .icongroup .icon").children().each(function(index) {
if ($(this).hasClass("menu")) {
var element = $(this);
$('<i class="fa fa-sort-asc arrow"></i>').appendTo($(this).prev());
// Add a click event to the element that contains a menu.
$(this).parent().click(function(e) {
// Stops click event from bubbling up to $(document)
e.stopPropagation();
// Do stuff
$(element).toggle().parent().addClass("active");
});
}
});
// Bind click event to document, to hide any .menu elements that are open
$(document).click(function() {
$('.menu').hide();
});
});
p/s: You should take advantage of chaining, so I combined the two lines referencing $(element) into a single one :) that is one of the most powerful features of jQuery, so go crazy :D
Update: OP asked to also detect click events within the menu item itself. This entails storing the toggle state somewhere, which I have chosen to use the data object for, and sniffing the toggle state on click before deciding to open/close the menu (and perform other actions, such as adding/removing class from parents):
$("#OfficeUI .ribbon .tabs > ul li[role=tab] .contents .group .icongroup .icon").children().each(function(index) {
if ($(this).hasClass("menu")) {
var element = $(this);
$('<i class="fa fa-sort-asc arrow"></i>').appendTo($(this).prev());
// Add a click event to the element that contains a menu.
$(this).parent().click(function(e) {
e.stopPropagation();
// Check toggle state
if(!$(this).data('state') || $(this).data('state') == 0) {
// If menu is closed, show it
$(element).show().parent().addClass('active');
// Update state
$(this).data('state', 1);
} else if ($(this).data('state') == 1) {
// If menu is already open, close it
$(element).hide().parent().removeClass('active');
// Update state
$(this).data('state', 0);
}
});
}
});
$(document).click(function() {
$('.menu').each(function() {
$(this).hide().parent().data('state', 0).removeClass('active');
});
});
See updated fiddle here: http://jsfiddle.net/teddyrised/mwCCt/5/
Bind a click event on the document, check that the target of the click is not the icons, then hide the element as needed
jQuery(document).on("click",function(e){
if( !jQuery(e.target).hasClass("icon") ) {
//hide ribbon code here
}
});
Add a event listener when you show it that is basically
$(document).on('click', function(){
//hide dropdown
});
You will want to destroy this event listener after you hide
without being tested... something like this
$("#OfficeUI .ribbon .tabs > ul li[role=tab] .contents .group .icongroup .icon").children().each(function(index) {
if ($(this).hasClass("menu")) {
var element = $(this);
$('<i class="fa fa-sort-asc arrow"></i>').appendTo($(this).prev());
// Add a click event to the element that contains a menu.
$(this).parent().click(function() {
$(element).toggle();
var dd = $(element).parent();
dd.addClass("active");
hideDD = function(){
dd.removeClass('active');
$(document).removeEventListener('click',hideDD);
};
$(document).addEventListener('click',hideDD);
});
}
});
Simply write the relevant code in the focus-out event of the element

Add extra class for link in Javascript [duplicate]

How can I do so that even "toggle_cart" is clickable in the same way as "clickerHeader"
but retains its hover effect (see arrow)?
please see http://jsfiddle.net/realitylab/STE48/3
$('.eventMenu > ul').toggleClass('no-js js');
$('.eventMenu .js ul').hide();
$(document).on("click", function(e) {
var $elem = $(e.target);
if ($elem.hasClass('clickerHeader')) {
$('.eventMenu .js ul').not($elem.next('ul')).hide();
$elem.next("ul").slideToggle();
} else if (!$($elem).parents('.contentHolderHeader').length) {
//} else {
$('.eventMenu .js ul').hide();
}
});
Just wrap both elements in a div ..
http://jsfiddle.net/STE48/5/
.
In the CSS add:
.eventMenu:hover .no-js .contentHolderHeader {
display: block;
}
Also add a display: none to div.eventMenu .contentHolderHeader.
Replace the JS with:
$('.eventMenu > ul').toggleClass('no-js js');
$(".toggle_cart").click(function(e){
$(".contentHolderHeader").slideToggle();
e.stopPropagation();
});
$(".eventMenu").click(function(e){
e.stopPropagation();
});
$(document).click(function(){
$(".contentHolderHeader").slideUp();
});
Remove the inner ul in the HTML.
Tested with/without JS: http://jsfiddle.net/vuF9n/2/
A minimal change to your existing code is to add the following two lines after the first line of your click function:
if ($elem.hasClass('toggle_cart'))
$elem = $elem.next();
In other words, if the span with the arrow is clicked, pretend that actually the anchor element was clicked. In context:
$(document).on("click", function(e) {
var $elem = $(e.target);
if ($elem.hasClass('toggle_cart'))
$elem = $elem.next();
if ($elem.hasClass('clickerHeader')) {
// etc.
Demo: http://jsfiddle.net/STE48/6/

Jquery execute action based on element's parent tag

I have the following:
<ul class="head clearfix">\
<li class=""><strong>Katalog firm <span>(3516)</span></strong></li>
<li class="">Katalog produktów <span>(23752)</span></li>
<li class="">Katalog uslug <span>(81)</span></li>
<li class="last">Katalog szkolen <span>(529)</span></li>
</ul>
I need to execute a mouseover effect only on links that do NOT have <strong> tag as parent. So in the example above I would skip "Katalog firm" link since that one has a tag as parent.
thanks
your css selector would be
ul.header > li > a.menuTabs:hover
to define the hover effect
if you need some jquery functionallity, you can use
$('ul.header > li > a.menuTabs')
to get those elements
Just filter out the elements that has a parent with the strong tag from the selector and attach the mouse events :
​$('ul.head li a')​.filter(function() {
return !$(this).parent().is('strong');
}).on('mouseenter', function() {
$(this).css('color','green');
});​
FIDDLE
This could be defined in CSS only by using the direct child selector >
ul.head > li > a.menuTabs:hover
{
/* hover style */
}
You can use the same selector in jquery
$('ul.head > li > a.menuTabs')
$('.head').on('click', 'li > a', function(evt) {
//do something
});
I'd suggest, as an alternative to using a more specific selector (probably the better option in most cases) checking the parent element in the mouseover event:
$('a').on('mouseover', function(e){
var that = this;
if ($(that).parent().is('strong')) {
return false;
}
else {
// do stuff.
}
});
$('.head > li > .menuTabs').mouseover(fucnction () {
// code here
});
Please use like this.
ul li>a:hover
{
background-color:black;
}​

Categories