IE only jQuery bug - javascript

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);

Related

Expand all / collapse all lists for class not working

I have a tree list where the user can open or close the branches by clicking. The tree has many levels and sub-levels. I want to be able to collapse all branches (down to the first level) or expand all branches for the class "my-clickable" when the user clicks on the Collapse All or Expand All button.
I can get the individual branches to close or open. But I can't get them all to open or close at once.
Here is my jsfiddle:
https://jsfiddle.net/dyrh325f/65/
One thing to note is that the Expand All button is being used after an ajax call that populates the entire tree. I have the 'Expand All' code in the "done" portion of the ajax call, so it applies to the generated html.
I want the user to be able to expand the entire tree out after he has closed one or more nodes. I've tried several variations of what I have in my jsFiddle. Nothing seems to work. What am I doing wrong?
I am a jQuery newbie so - thanks for any and all help!!!
Code:
HTML:
Expand Tree
<font face="calibri" size="3pt" >
<ul id="tree1" class="mytree">
<li id="theJob" class="my-clickable mytree liParOpen">My Job</li>
<ul id="treeList" class="mytree" name="treeList">
<li id='0' class='mytree child-click'>Batcher</li>
<li id='1' class='mytree child-click'>Retriever</li>
<li id='2' class='my-clickable mytree liParOpen'> TASK 2</li>
<ul class='mytree'>
<li id='2a' class='mytree child-click'>Prep1</li>
<li id='2b' class='mytree child-click'>Prep2</li>
<li id='2c' class='mytree my-clickable liParOpen'>PREP3</li>
<ul class='mytree'>
<li id='2b1' class='mytree child-click'>PREP3a</li>
<li id='2b2' class='mytree child-click'>PREP3b</li>
</ul>
</ul>
<li id='3' class='mytree child-click' > task 3</li>
<li id='4' class='my-clickable mytree liParOpen'> TASK 4</li>
<ul class='mytree'>
<li id='4a' class='mytree child-click'>Edit1</li>
<li id='4b' class='mytree child-click'>Edit2</li>
</ul>
<li id='5' class='my-clickable mytree liParOpen'> TASK 5</li>
<ul class='mytree'>
<li id='5a' class='mytree my-clickable liParOpen'>Del1</li>
<ul class='mytree'>
<li id='5a1' class='mytree child-click'>Del1a</li>
<li id='5a2' class='mytree child-click'>Del1b</li>
</ul>
<li id='5b' class='mytree child-click'>Del2</li>
</ul>
<li id='6' class='mytree child-click'>DocEjector-3</li>
</ul>
</ul>
</font>
Jquery:
$(document).ready(function(){
// expand button
$('#expandTree').on('click', function(event) {
var all = $('.my-clickable');
var closed = all.filter('.liParClosed');
closed.find("ul").each(function(){
$(this).next('ul').attr("class", "liParOpen");
$(this).nextAll('ul').toggle();
});
}); // end expand button
// collapse button
$('#collapseTree').on('click', function(event) {
var all = $('.my-clickable');
var open = all.filter('.liParOpen');
open.find("ul").each(function(){
$(this).next("ul").attr("class", "liParClosed");
$(this).nextAll('ul').slideToggle();
});
}); // end collapse button
// this is the top most level parents
$(".my-clickable").on('click', function(event) {
var taskId=$(this).closest("li").attr('id');
var tsk = '#'.concat(taskId);
if (taskId != "theJob") {
if ($(tsk).next('ul').length <= 0) {
$(tsk).toggleClass("liParOpen liParClosed");
$(tsk).next('ul').slideToggle();
}
else {
//$(event.target).find('ul').toggle();
$(tsk).toggleClass("liParOpen liParClosed");
$(tsk).children('li').slideToggle();
$(tsk).next('ul').slideToggle();
}
} // end if taskId != "theJob"
else {
$(tsk).toggleClass("liParOpen liParClosed");
$(tsk).slideToggle();
}
event.cancelBubble=true;
event.stopPropagation();
});
//2nd level parents
$(".my-clickable").on('click', ".my-clickable", function(event) {
var taskId=$(this).closest("li").attr('id');
var tsk = '#'.concat(taskId);
//$(event.target).find('ul').slideToggle();
$(tsk).toggleClass("liParOpen liParClosed");
//event.cancelBubble=true;
event.stopPropagation();
});
// first level child w/no children (parent=job)
$(".child-click").on('click', function(event) {
event.stopPropagation();
});
});
CSS:
ul.mytree li.liParClosed {
background-color: green;
fontWeight: normal;
}
ul.mytree li.liParOpen {
background-color: cyan;
fontWeight: normal;
}
.selected{
border: 3px solid yellow;
background-color: yellow;
fontWeight: bold;
}
ul.mytree liParOpen selected{
border: 3px solid red;
background-color: yellow;
fontWeight: bold;
}
ul.mytree li selected{
border: 3px solid red;
background-color: yellow;
fontWeight: bold;
}
ul.mytree li {
background-color: white;
fontWeight: normal;
}
ul.mytree {
background-color: white;
fontWeight: normal;
}
You almost got it but the toggling of classes "liParClosed" and "liParOpen" wasn't being done right.
Here's a fiddle fixing that issue:
JS Fiddle
Relevant code changes:
// expand button
$('#expandTree').on('click', function(event) {
var all = $('.my-clickable');
var closed = all.filter('.liParClosed');
closed.each(function(){
$(this).removeClass('liParClosed').addClass('liParOpen');
$(this).next('ul').slideToggle();
});
}); // end expand button
// collapse button
$('#collapseTree').on('click', function(event) {
var all = $('.my-clickable');
var open = all.filter('.liParOpen');
open.each(function(){
$(this).removeClass('liParOpen').addClass('liParClosed');
$(this).next('ul').slideToggle();
});
}); // end collapse button
Look at the way the classes are being added/removed. Also, you were
looking for open.find("ul") BUT open itself had to be looped through as it is a list of open li's.
Hope this helps. :)

a little trouble with mouseover event

my codes are working correctly but in the change of requirement.
i need to keep current active class active which is removed again i mouseover at active <li>
i am not good in JQuery so can somebody help doing this??
(function($){
$(document).ready(function(){
$('a.form').on('mouseover', function(event) {
event.preventDefault();
event.stopPropagation();
$(this).parent().siblings().removeClass('active');
$(this).parent().toggleClass('active');
});
});
})(jQuery);
li.active .btncss, .btncss:hover {
color: #FFAE00;
background: #ffffff;
border: 2px solid;
text-decoration: none;
}
li{display:inline}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="list-inline">
<li class="active">With In City</li>
<li class="">Full Day Hire</li>
<li class="">Half Day Hire</li>
<li class="">InterCity Hire</li>
</ul>
Use addClass instead of toggleClass.
.toggleClass Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the state argument.
.addClass() Adds the specified class(es) to each element in the set of matched elements.
It's important to note that this method does not replace a class. It simply adds the class, appending it to any which may already be assigned to the elements.
Try this:
(function($) {
$(document).ready(function() {
$('a.form').on('mouseover', function(event) {
event.preventDefault();
event.stopPropagation();
$(this).parent().siblings().removeClass('active');
$(this).parent().addClass('active');
});
});
})(jQuery);
li.active .btncss,
.btncss:hover {
color: #FFAE00;
background: #ffffff;
border: 2px solid;
text-decoration: none;
}
li {
display: inline
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="list-inline">
<li class="active">With In City
</li>
<li class="">Full Day Hire
</li>
<li class="">Half Day Hire
</li>
<li class="">InterCity Hire
</li>
</ul>
It's a little hard to understand your question -- do you want the <li> that is originally active to be reset as active on mouseout? To do this, you need to record what was originally active and add a mouseout handler:
(function($){
$(document).ready(function(){
var originally_active = $('li.active');
$('a.form').on('mouseover', function(event) {
event.preventDefault();
event.stopPropagation();
$(this).parent().siblings().removeClass('active');
$(this).parent().toggleClass('active');
});
$('a.form').on('mouseout', function() {
$(this).parent().removeClass('active');
originally_active.toggleClass('active');
});
});
})(jQuery);
li.active .btncss, .btncss:hover {
color: #FFAE00;
background: #ffffff;
border: 2px solid;
text-decoration: none;
}
li{display:inline}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="list-inline">
<li class="active">With In City</li>
<li class="">Full Day Hire</li>
<li class="">Half Day Hire</li>
<li class="">InterCity Hire</li>
</ul>

Refresh list after adding an item with jQuery UI sortable plugin

Alright so I have a nested sortable list, each item is therefore both a container and a sortable element.
The problem I am facing is that, whenever I add a new element, I want jQuery to refresh its internal state with the new item.
According to the documentation, one has to call the sortable method passing as parameter 'refresh', but still I can't make it work.
Sample code:
http://jsfiddle.net/X5sBm/
JavaScript:
$(document).ready(function() {
var list = $('#mycontainer ul').sortable({
connectWith: '#mycontainer ul',
placeholder: 'myplaceholder'
});
function addElement(text) {
$('#mycontainer > ul').append('<li>' + text + '<ul></ul></li>');
list.sortable('refresh');
}
addElement('yolo');
});
HTML:
<div id="mycontainer">
<ul>
<li>
Some text
<ul>
</ul>
</li>
<li>
Some text 2
<ul>
</ul>
</li>
</ul>
</div>
CSS:
#mycontainer > ul {
display: block;
}
#mycontainer > ul ul {
min-height: 10px;
padding-left: 20px;
}
.myplaceholder {
background-color: yellow;
}
Try to drag one pre existing item under the newly added one, you won't be able to do so even after the refresh.
I found a cheap fix:
I call sortable again on the same tag reinitialising the Sortable plugin like so:
$('#mycontainer ul').sortable({
connectWith: '#mycontainer ul',
placeholder: 'myplaceholder'
});
and it works.
The key understanding and take-away is to make the dynamically created <ul></ul> initialized with the sortable options. I suggest making an options object for easy re-use.
$(document).ready(function() {
const container = $('#mycontainer ul');
const options = {
connectWith: '#mycontainer ul',
placeholder: 'myplaceholder'
};
container.sortable(options);
function addElement(text) {
let newListItem = $('<li>').text(text);
newListItem.append($('<ul>').sortable(options)); // <- key concept
$('#mycontainer > ul').append(newListItem);
//container.sortable('refresh');
}
addElement('yolo');
});
#mycontainer > ul {
display: block;
}
#mycontainer ul ul {
min-height: 10px;
padding-left: 20px;
}
.myplaceholder {
background-color: yellow;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="mycontainer">
<ul>
<li>
Some text
<ul>
</ul>
</li>
<li>
Some text 2
<ul>
</ul>
</li>
<li>
Some text 3
<ul>
</ul>
</li>
</ul>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
</body>
</html>

How to cancel a CSS style set by 'hover child' selector on event?

I am making a CSS based menu, with submenu items that pop up when the root element is hovered. the problem I have is that I want the CSS menu to close when I click an item in the list, but at that point I am still technically hovering over the top element, so I figured I had to use javascript to hide the menu. But when I set the display property, I set it forever and it overrides the hover selector of the parent node. And so the submenu doesn't show up anymore.
This must be pretty common, but I can't find any answers...
Any help much appreciated!
html:
<ul class="level1">
<li>one
<ul class="level2">
<li id="test">two</li>
<li>three</li>
</ul>
</li>
</ul>
css:
.level1 li:hover > ul {
display: inline;
}
.level2 {
display: none;
}
js:
document
.getElementById('test')
.addEventListener('click',function () {
this.parentNode.style.display = 'none';
// After this the menu doesn't open anymore
// because the style is overriden
});
Here's the jsfiddle
You can try this.
<ul class="level1">
<li class="hoverMe">one
<ul class="level2">
<li id="test">two</li>
<li>three</li>
</ul>
</li>
</ul>
.hoverMe:hover > ul {
display: inline;
}
var test = document.getElementById('test');
test.onclick = function () {
this.parentNode.parentNode.className = "";
};
var level1 = document.getElementsByClassName('level1')[0];
level1.getElementsByTagName("li")[0].onmouseover = function () {
if (this.className != "hoverMe") {
this.className = "hoverMe";
}
};

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;
}

Categories

Resources