Add class to current and previous list items - javascript

I have an unordered list used as a menu to play sections of a video. The list item (video portion) that's playing highlights when clicked by adding a class to it and removing the class from any other list item. I've been asked by the client to change this so that if you click on an item, the previous list items are selected also. Namely if you click on Vid 2, then Vid 1 and Start are given the 'selected' class as well as Vid 2. Clicking on Vid 3, would highlight Vid 3, Vid 2, Vid 1, Start and so on.
I would also like to remove the selected class from any list item that's ahead of the clicked item. For instance if Vid 4 is selected and the user clicks on Vid 2, then the selected class is removed from Vid 4 and Vid 2, Vid 1 and Start have the class selected. Sorry if any of this makes little sense and thanks in advance.
$("#select li a").click(function() {
$(this).parent().addClass('selected').siblings().removeClass('selected');
});
#video-controls ul li {
cursor: pointer;
}
#video-controls ul li a {
color: #5f6a72;
text-decoration: none;
display: block;
}
#video-controls ul li.selected,
#video-controls ul li.selected a {
color: #00aad2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<nav id="video-controls">
<ul id="select">
<li id="start">Start</li>
<li id="vid-1">Vid 1</li>
<li id="vid-2">Vid 2</li>
<li id="vid-3">Vid 3</li>
<li id="vid-4">Vid 4</li>
</ul>
</nav>

You can
1) traverse and get all previous elements.
2) use .andSelf() to add clicked object parent
3) add class selected to all elements returned above.
4) find remaining siblings other than returned element in step 2
5) remove class selected
$("#select li a").click(function() {
$(this).parent().prevAll().andSelf().addClass('selected').filter(':last').nextAll().removeClass('selected');
});
#video-controls ul li {
cursor: pointer;
}
#video-controls ul li a {
color: #5f6a72;
text-decoration: none;
display: block;
}
#video-controls ul li.selected,
#video-controls ul li.selected a {
color: #00aad2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<nav id="video-controls">
<ul id="select">
<li id="start">Start</li>
<li id="vid-1">Vid 1</li>
<li id="vid-2">Vid 2</li>
<li id="vid-3">Vid 3</li>
<li id="vid-4">Vid 4</li>
</ul>
</nav>

Related

Show next sibling element if is previous element hovered with pure JavaScript

I have this snippet of html
ul { display:none }
test 1
test 2
test 3
<ul class="hh-ul-1 mm-listitem">
sub 1
</ul>
Test 4
<ul class="hh-ul-1 mm-listitem">
sub 2
</ul>
By default ul elements are hidden.
I'm trying to achieve when the a element is hovered to show only ul element which is the next sibling.
For example, if test 3 link has hovered, the ul element with sub 1 link needs to show up and to stay while the a and ul are hovered.
I'm not so experienced, so any help would be appreciated.
Have a look at adjacent sibling combinator
I add focus so you can keep the UL open
Also the a in the UL should be in an li. That makes it somewhat harder to make a sub-sub menu
ul { display:none; position: absolute; top:50px }
a:hover + ul { display: block }
a:focus + ul { display: block }
test 1
test 2
test 3
<ul class="hh-ul-1 mm-listitem">
<li>sub 1</li>
</ul>
Test 4
<ul class="hh-ul-1 mm-listitem">
<li>sub 2</li>
</ul>

Multiple dropdown navigation - how to change the trigger button

I am struggling with one of my dropdowns.
Currently it is set up to be triggered by an i tag to drop down the sub menu.
$('nav li i').click(function() {
I want to change it to (nav li a) so it is not the icon that has to be pressed
I also have the code:
var child = $(this).index('nav ul li i');
but i am not sure what to change this to?
You can see all the code in jsfiddle.
http://jsfiddle.net/susannalarsen/VNYAx/
Since I do not see the <a> element anywhere, I have changed the <i> to <a> for demonstration purposes. You can see the example on http://jsfiddle.net/VNYAx/3/
Basically I changed
$('nav li i').click(function() {
to
$('nav li a').click(function() {
And also
var child = $(this).index('nav ul li i');
to
var child = $(this).index('nav ul li a');
Is this what you need?
Instead of using the index of the icon as a way to identify which dropdown you want to slide down, you can save a reference to that dropdown by searching for '.dropdown' within the element clicked.
$('nav li').click(function () {
var $childDropdown = $(this).find('.dropdown');
if ($childDropdown.is(':visible')) {
$('.dropdown').slideUp(300);
} else {
$('.dropdown').slideUp(300);
$childDropdown.slideDown(300);
}
});
http://jsfiddle.net/9Fk7j/2/
Just an alternative approach to this problem using .slideToggle(). If you need anything explaining please comment and I will edit the answer. I have commented the JavaScript below. I also removed the extra <div> in the markup as the nested <ul> is a perfectly good container.
Demo
HTML
<nav id="moo">
<ul>
<li>Item A
<ul>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</li>
<li>Item B
<ul>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
</ul>
</li>
</ul>
</nav>
CSS
ul {
padding: 0;
margin: 0;
}
li {
display: inline;
}
nav li {
position: relative;
}
ul ul {
display: none;
position: absolute;
border:1px solid #ccc;
padding: 10px;
}
ul ul li {
display: block;
}
JavaScript
var $allSubMenus = $('ul ul'); // handle to all submenus
$('nav li a').click(function (e) {
var $li = $(this).closest('li'); // get parent <li> of the <a> clicked on
var $subMenu = $li.find('ul'); // get our submenu
$allSubMenus.not($subMenu).slideUp(300); // hide all other submenus
$subMenu.slideToggle(300); // show our submenu
e.stopPropagation(); // stop event bubbling further
});
$(document).click(function () {
$allSubMenus.hide(); // no need to reselect, just use handle
});

Activate dropdown with pure javascript

Right now I have a pure HTML CSS navigation bar with some dropdowns. However on ipad the hover will obviously not work.
I want to add a click event to the relevant menu items so the dropdown will also activate with an onclick event.
I've look at other answers but I'm not capable of reading javascript well enough so that I can modify them for my specific site.
here is a link to where I'm at now: http://2ftrade.nl/kareem/eindopdracht/
and this is the relevant html. In my css the default is display:none for the dropdown menus and is changed to display:block when hovered over the li that contains it.
<ul>
<li>Home</li>
<li><a title="">Opleiding</a>
<!-- the dropdown -->
<ul>
<li>Visie & Beleid</li>
<li>Opbouw Studieprogramma</li>
<li>Competenties</li>
<li>Diploma</li>
<li>Beroepen</li>
</ul>
</li>
<li>Onderwijsprogramma</li>
<li>Organisatie</li>
<li><a title="">Stages en Projecten</a>
<!-- another dropdown -->
<ul>
<li>Stages</li>
<li>Projecten</li>
</ul>
</li>
<li>Top</li>
</ul>
This is the css that hides the dropdown section
nav > ul > li > ul {
display: none;
position: absolute;
}
and this is what will display it when hovering
nav > ul > li:hover ul {
display: block;
}
you can attach event listener to your element:
var dropdown_button = document.getElementById('#your-button-that-activates-dropdown');
dropdown_button.addEventListener('click', function() {
//here do what you want to do when the button is clicked.
}, false);
you should use javascript events , some thing like this :
var btn = document.getElementById('btn') // this button is a key to run what you want
var drp = document.getElementById('drp') // this is your dropdown list
btn.onclick = function()
{
drp.style.display = 'block'
// other codes . . .
}
You can achieve this without using javascript.
Use the :target selector
example
Add an id and href for each target in the html
<ul>
<li>Home</li>
<li id="Opleiding">
<a title="" href="#Opleiding">Opleiding</a>
<!-- the dropdown -->
<ul>
<li>Visie & Beleid</li>
<li>Opbouw Studieprogramma</li>
<li>Competenties</li>
<li>Diploma</li>
<li>Beroepen</li>
</ul>
</li>
<li>Onderwijsprogramma</li>
<li>Organisatie</li>
<li id="StagesenProjecten">
Stages en Projecten
<!-- another dropdown -->
<ul>
<li>Stages</li>
<li>Projecten</li>
</ul>
</li>
<li>Top</li>
</ul>
in the css specify the style for the :target
nav > ul > li:target ul {
display: block;
}
nav > ul > li:hover ul {
display: block;
}
nav > ul > li > ul {
display: none;
position: absolute;
}

Show headers only for collections that are not empty

I have a couple of lists like this:
<ul>
<li class="list-header">Header</li>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
By some rules I hide and show <li> items so sometimes the list has visible <li> and sometimes it has no visible <li> elements at all except the one with list-header class, so <li class="list-header"> is still there. I want to hide that header if there are no <li> visible elements in it under header. Though I want the <ul> still to be visible.
How do I do that?
What you could do (demo):
$('ul').each(function() {
$ul = $(this);
$ul.find('.list-header').toggle($ul.has('li:not(.list-header):visible').length != 0);
});
Basically, what the above does is toggling the .list-header (I've wrapped it in the .each() in order to demo different lists) depending on whether the list .has() :visible li elements that are :not(.list-header).
UPDATE
Now it works. Sorry.
You could use the :visible and :not selectors to see if there are any elements present when you change the visibility. This example toggles the visibility when clicking the elements, and hides the header if there are no elements present:
$('li:not(".list-header")').click(function(){
$(this).toggle(10,function(){
var l = $(this).parent().children('li:visible:not(".list-header")').length
if (l>0) $(this).parent().children('li.list-header').show();
else $(this).parent().children('li.list-header').hide();
});
});
working example: http://jsfiddle.net/LDG4J/4/
There is no lh element in HTML. References: HTML5, HTML4.01, HTML 3.2. (You've removed the lh from the question.)
Instead, use an li with a class you style as you see fit (or if you're targeting recent-enough browses, no class required; just style li:nth-child(1) or li:first-child), and just don't hide that li (which will keep the ul visible):
<ul>
<li class='header'>Header</li>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
Update: I may have misunderstood. If you want to hide the header but keep the ul visible in some way:
A ul with no visible li elements will typically be invisible because it won't have any dimensions. You can override that with CSS, styling the ul to have a specific size (live example):
CSS:
ul.foo {
width: 5em;
height: 5em;
background-color: #eee;
border: 1px solid #aaa;
}
HTML:
<p><code>ul</code> with no visible children:</p>
<ul class='foo'>
<li style="display: none">This is hidden</li>
</ul>
<p><code>ul</code> with a visible child:</p>
<ul class='foo'>
<li>Visible child, note that it wraps</li>
</ul>
And of course you can apply that via jQuery rather than with a static CSS rule:
$("ul.foo").css({
width: "5em",
height: "5em",
backgroundColor: "#eee",
border: "1px solid #aaa"
});
...so you could do that when you're hiding all of the ul's elements, and undo it when showing at least one of them. After making a change:
var ul = $(/*...selector for the relevant list...*/);
if (ul.find('li:visible')[0]) {
// There's at least one visible `li` child
ul.css({/*...styles for when the list is not empty...*/});
}
else {
// There are no visible `li` children
ul.css({/*...styles for when the list is empty...*/});
}
...or better yet, add/remove a class.
try this:
$("ul li:not('.list-header')").each(function(index, val) {
if ($(this).text() == '') {
$(this).hide();
}
});
if (! ($('ul').has("li:visible:not('.list-header')").length)) {
$('li.list-header').hide();
}

IE only jQuery bug

demo for reference: http://greg-j.com/static-content/accordian/
When viewed in any other browser, the toggler works as you would expect it to. However, in IE (I'm using 8), you can only contract the currently expanded sub menu, and then you get undesired results once it is closed.
Example html:
<!DOCTYPE html>
<html>
<head>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled</title>
<link type="text/css" rel="stylesheet" href="http://meyerweb.com/eric/tools/css/reset/reset.css" />
<style type="text/css">
body{margin: 50px; font-family: arial; font-size: 12px}
div{width: 172px; margin: 0 auto;}
a{text-decoration: none; color: #006ecf}
.gMenu{
background: #fafbfc;
border-left: solid 1px #abc1d6;
border-right: solid 1px #abc1d6;
padding: 5px 0;
}
.gMenu ul{
background: white;
border-top: solid 1px #dbe6f0;
border-bottom: solid 1px #dbe6f0;
position: relative;
top: 4px;
}
.gMenu li{
line-height: 16px;
padding: 4px 0;
position: relative;
}
.gMenu li li{
padding-left: 10px;
font-size: 11px;
}
.gMenu a{
padding: 0 4px;
}
.gMenu ul a{
padding: 0;
}
.gMenu a:hover{
color: #000;
text-decoration: underline;
}
.gMenu em{
color: #abc1d6;
}
</style>
</head>
<body>
<div>
<ul class="gMenu">
<li class="gm-active">Bodywork
<ul>
<li>Fairing Bolt Kits <em>(23)</em></li>
<li>Fairing Brackets <em>(2)</em></li>
<li>Fairings <em>(17)</em></li>
<li>Fender Eliminator Kits <em>(6)</em></li>
<li>Front Fenders <em>(5)</em></li>
<li>Graphics Kits <em>(223)</em></li>
<li>Huggers <em>(40)</em></li>
<li>Under Trays <em>(7)</em></li>
<li>Windscreens <em>(3)</em></li>
</ul>
</li>
<li>Books
<ul>
<li>Service Manuals</li>
</ul>
</li>
<li>Brakes
<ul>
<li>Brake Hardware</li>
<li>Brake Lines</li>
<li>Brake Pads</li>
<li>Brake Rotors</li>
</ul>
</li>
<li>Controls
<ul>
<li>Bar Ends</li>
<li>Cables</li>
<li>Handlebars</li>
<li>Levers</li>
<li>Mirrors</li>
<li>Pedals</li>
<li>Quick Shift Kits</li>
<li>Rearsets</li>
<li>Stabilizers</li>
<li>Throttles</li>
<li>Triple Clamps</li>
</ul>
</li>
<li>Caps, Covers, Guards
<ul>
<li>Axle Covers</li>
<li>Chain Guards</li>
<li>Heel Guards</li>
<li>Mirror & Signal Block Offs</li>
<li>Oil Caps and Dipsticks</li>
<li>Reservoir Covers</li>
</ul>
</li>
<li>Drive
<ul>
<li>Chain and Sprocket Kits</li>
<li>Front Countershaft Sprockets</li>
<li>O-Ring Chains</li>
<li>Rear Drive Sprockets</li>
<li>Roller Chains</li>
</ul>
</li>
<li>Electrical
<ul>
<li>Accessory Lighting</li>
<li>Alarms</li>
<li>Batteries</li>
<li>Gauges</li>
<li>Marker Lights</li>
<li>Radar Detectors</li>
<li>Spark Plugs</li>
<li>Taillights</li>
<li>Turn Signals</li>
</ul>
</li>
<li>Engine
<ul>
<li>Clutch Discs</li>
<li>Clutch Kits</li>
<li>Clutch Springs</li>
<li>Oil Filters</li>
</ul>
</li>
<li>Exhaust
<ul>
<li>Complete Exhaust Systems</li>
<li>Heat Shields</li>
<li>Slip-On Mufflers</li>
</ul>
</li>
<li>Frame & Swingarm
<ul>
<li>Frame Sliders</li>
<li>Kickstands</li>
<li>License Plate Kits</li>
<li>Stand Adapters</li>
<li>Swingarm Extensions</li>
<li>Swingarm Spools</li>
</ul>
</li>
<li>Fuel Systems
<ul>
<li>Fuel Injection Mapping</li>
</ul>
</li>
<li>Suspension
<ul>
<li>Fork Seals</li>
<li>Rear Lowering Kits</li>
<li>Shock Absorbers</li>
</ul>
</li>
<li>Tanks
<ul>
<li>Gas Caps</li>
<li>Tank Bras</li>
<li>Tank Protectors</li>
<li>Traction Pads</li>
</ul>
</li>
<li>Intake
<ul>
<li>Air Filters</li>
<li>Velocity Stacks</li>
</ul>
</li>
<li>Tires
<ul>
<li>Front Tires</li>
<li>Rear Tires</li>
</ul>
</li>
</ul>
</div>
</body>
</html>
Example JS:
(function($){
$.fn.gMenu = function(options) {
var o = $.extend({
speed: "fast"
}, options);
return this.each(function() {
var $ul = $('ul', this), // Get this ul's decendant ul's
$li = $(this).children('li'); // Get only the first decendant li's in this ul
$ul.not('.gm-active ul').hide();
// Create toggler elements for the first decendant li's only
$li.children('a').after(
$('<span>', { html: "toggle", 'class': "gm-toggler"}).hide()
);
var $toggler = $li.find('.gm-toggler');
// Only show the toggler when a collapsed menu item is hovered
$li.hover(function(){
$('.gm-toggler', this).toggle();
});
// Only show one child menu at a time. Never collapse them all
$toggler.click(function(){
// get this togglers parent ul
var $current = $(this).parent().find('ul');
// slide all other child menu up and remove its .active class
$($ul).not($current).slideUp(o.speed)
.parent().removeClass('gm-active');
// Stop sliding this togglers menu if it is animating and slide it the other direction
$current.stop(false,true).slideToggle(o.speed);
// If this togglers menu is active ...
$(this).parent('li').toggleClass('gm-active');
});
});
};
})(jQuery);
$(document).ready(function(){
$('.gMenu').gMenu({
speed: 200
});
});
Maybe this can point you in the right direction.
You don't need a return this.each(function() {}); since you are already passing the DOM element in the plug-in aka "gMenu" you just work from there on.
For brevity I left out a lot of code and minimized it to the features that matter.
Also you don't want to hide other opened menu items, since it breaks common patterns in UI design.
(function($){
$.fn.gMenu = function(options) {
var o = $.extend({
speed: "fast"
}, options);
$('ul', this).not('.gm-active ul').hide();
$('a', this).click(function(){
$(this).next().slideToggle(o.speed);
});
return this;
};
})(jQuery);
Solution found.
The issue was that I wasn't specifically cycling through each list element found. I reworked the jQuery a bit to put everything inside an $.each() function and now all seems well. I'm not sure if this is the most efficient way of doing it now though, so if anyone see's any room for improvement, please let me know.
http://jsbin.com/edago3/8/
(function($){
$.fn.extend({
gMenu: function (options) {
var o = $.extend({
speed: "fast"
}, options);
// Get this ul's decendant ul's
var $ul = $('ul', this);
// Hide decendant ul's that are not active
$ul.not('.gm-active ul').hide();
// loop through this lists list elements
$.each($(this).children('li'), function(){
// Create toggler elements for the first decendant li's only
$(this).children('a').after(
$('<span>', { html: "toggle", 'class': "gm-toggler"})
);
// find the togglers and hide them
var $toggler = $(this).find('.gm-toggler').hide();
// Only show the toggler when a menu item is hovered
$(this).hover(function(){
$('.gm-toggler', this).toggle();
});
// Only show one child menu at a time. Never collapse them all
$($toggler, this).click(function(){
// get this togglers parent ul
var $current = $(this).parent().find('ul');
// slide all other child menu up and remove its .active class
$($ul).not($current).slideUp(o.speed)
.parent().removeClass('gm-active');
// Stop sliding this togglers menu if it is animating and slide it the other direction
$current.stop(false,true).slideToggle(o.speed);
// If this togglers menu is active ...
$(this).parent('li').toggleClass('gm-active');
});
});
}
});
})(jQuery);

Categories

Resources