slideDown jQuery Navigation divs - javascript

I'm trying to get something like the navigation on the following site to work.link
I have the following code so far on JSFIDDLE and have it kind of working. The only issue is when hovering over the submenu li's for a second time (or cycling through them) the new background div does not drop down again.
Any suggestions much appreciated - thanks
EDIT:
I've simplified the demo here jsfiddle.net/XLZGP/11 to just the main menu items. I'm looking for the new colour to slide in while the old colour does NOT slideup.
code as follows
HTML
<header>
<div class="inner-header">
<nav>
<ul>
<li class="parent"> bus
<div class="locations-wrapper">
<ul class="sub-menu">
<li>station
</li>
<li>stop
</li>
</ul>
</div>
</li>
<li class="parent"> train
<div class="locations-wrapper">
<ul class="sub-menu">
<li>station
</li>
<li>stop
</li>
</ul>
</div>
</li>
</ul>
</nav>
</div>
<div class="boxes">
<div class="bus"></div>
<div class="bus-station"></div>
<div class="bus-stop"></div>
<div class="train"></div>
<div class="train-station"></div>
<div class="train-stop"></div>
</div>
</header>
JS
var parentList = $('.parent');
var currentTitle;
var currentChildTitle;
parentList.on('mouseover', function () {
$(this).find('.locations-wrapper').slideDown(400);
currentTitle = $(this).find('a').attr('title');
$('.' + currentTitle).addClass('open').slideDown(400);
});
parentList.on('mouseleave', function () {
$(this).find('.locations-wrapper').slideUp(400);
$('.boxes').children('div').removeClass('open').slideUp(400);
});
$('.sub-menu').on('mouseover', 'li', function () {
currentChildTitle = $(this).find('a').attr('title');
currentChildTitle = currentTitle + '-' + currentChildTitle;
$('.boxes').children('div').removeClass('open');
$('.' + currentChildTitle).addClass('open').slideDown(400);
});`

I think what you are looking for is something closer to this, http://jsfiddle.net/un5ke/1/
var mouseLeft = true;
$('.sub-menu').on('mouseleave', 'li', function () {
mouseLeft = true;
setTimeout(function() {
if (mouseLeft) {
var toClose = $('.boxes').children('div.open');
toClose.slideUp();
toClose.removeClass('open');
}
}, 500);
});
$('.sub-menu').on('mouseover', 'li', function () {
mouseLeft = false;
currentChildTitle = $(this).find('a').attr('title');
currentChildTitle = currentTitle + '-' + currentChildTitle;
var toOpen = $('.' + currentChildTitle);
if (!toOpen.hasClass('open')){
var toClose = $('.boxes').children('div.open');
toOpen.addClass('open').slideDown(400, function completed () {
$(toClose).slideUp();
$(toClose).removeClass('open');
});
}
});
There is a couple of things that I have done:
Removed z-index from the open class
Added a half-second delay to a mouse leave, so that if the person is mousing from one to another the background doesn't go crazy.
Removed the mouseleave of the entire menu becuase that isn't the issue at the moment.
Double-checking before opening that the current menu isn't already opened.

You don't have a mouseleave handler for your sub menu items, so once they're down, they're down.
I added a quick mouseleave for the sub-menu's too in a fiddle - should be a starter for the exact effect you want to achieve:
http://jsfiddle.net/XLZGP/
$('.sub-menu').on('mouseleave', 'li', function () {
currentChildTitle = $(this).find('a').attr('title');
currentChildTitle = currentTitle + '-' + currentChildTitle;
$('.' + currentChildTitle).removeClass('open').slideUp(400);
});

Took a different approach and decided to create the slide down div backgrounds dynamically through JS - creating each dom node and then assigning it to the dom, limiting the total number produced.
Overlaying each div with the next. WORKING JSFIDDLE
var parentList = $('.parent');
// declare variables
var currentChildTitle;
var currentTitle;
var banner;
// setup append div function
function dropBanner(currentTitle) {
// create the banner variable dom node
banner = $('<div class="' + currentTitle + '"/></div>');
// add it to the dom
$('.boxes').append(banner);
// animate it
$('.' + currentTitle).slideDown(300);
}
// setup a function to limit the number of divs appended
function chop() {
if ($('.boxes div').length > 15) {
$('.boxes div').eq(0).remove();
}
}
// listen for mouseover the parent list items
parentList.on('mouseover', function (e) {
if (!($(this).find('.locations-wrapper').is(':visible'))) {
$(this).find('.locations-wrapper').slideDown(300);
}
// grab the current list item title
currentTitle = $(this).find('a').attr('title');
// call dropBanner passing the current list item title
dropBanner(currentTitle);
chop();
});
// listen for mouseleave
parentList.on('mouseleave', function () {
$(this).find('.locations-wrapper').delay(300).slideUp(300);
$('.boxes div').delay(300).slideUp(300);
});
// listen for mouseover the submenu list items
$('.sub-menu').on('mouseover', 'li', function (e) {
// grab the current list item title
currentTitle = $(this).find('a').attr('title');
// call dropBanner passing the current list item title
dropBanner(currentTitle);
chop();
// stop the bubbling up effect to parent list items
e.stopPropagation();
});

Related

jQuery function non-reversible. [website cart feature]

I'm pretty new to the world of jQuery and having some difficulty with a cart feature I am trying to add to a website.
I have created a function to add an element (based on its id) to the cart. This works fine. However when I try to reverse it (say someone clicks on the cart icon again to remove it) the cart count increases more and the class doesn't change back.
You will see in my code I am changing the classes for visual representation (unselected = svg with no fill & selected = svg with fill).
I have tried toggling the class, removing and adding the class but I can't think of much more. Any help would be greatly appreciated!
$(document).ready(function() {
var cart = [];
$("a.addToCart").click(function(event) {
var pressedId = event.target.id;
$("#cart_button").removeClass("hidden");
$("#" + pressedId).removeClass("addToCart");
$("#" + pressedId).addClass("addedToCart");
cart.push(pressedId)
$('.cart--counter').html(cart.length);
});
});
$(document).ready(function() {
$("a.addedToCart").click(function(event) {
var unpressedId = event.target.id;
$("#" + unpressedId).addClass("addToCart");
$("#" + unpressedId).removeClass("addedToCart");
cart.splice( $.inArray(unpressedID,cart) ,1 );
$('.cart--counter').html(cart.length);
});
});
Here is an example of the HTML with a class and ID.
<a id="12" class="addToCart">
Again, for clarification: the class changes appropriately from "addToCart" to "addedToCart" but is not reversible & the array is successfully updated with appropriate "ID" but can not be removed again.
Your issue is that when you add your event handlers, there are no elements with class addedToCart, so no event handlers get assigned. You need to use a delegated event handler instead:
var cart = [];
$(document).ready(function() {
$(document).on('click', "a.addToCart", function(event) {
var pressedId = event.target.id;
$("#cart_button").removeClass("hidden");
$("#" + pressedId).removeClass("addToCart");
$("#" + pressedId).addClass("addedToCart");
cart.push(pressedId)
$('.cart--counter').html(cart.length);
$('#cart').html(cart.toString());
});
});
$(document).ready(function() {
$(document).on('click', "a.addedToCart", function(event) {
var unpressedId = event.target.id;
$("#" + unpressedId).addClass("addToCart");
$("#" + unpressedId).removeClass("addedToCart");
cart.splice($.inArray(unpressedId, cart), 1);
$('.cart--counter').html(cart.length);
$('#cart').html(cart.toString());
});
});
.hidden {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a id="12" class="addToCart">Item 12</a><br />
<a id="13" class="addToCart">Item 13</a>
<div class="cart--counter">**</div>
<br />
<div id="cart"></div>
<br />
<div id="cart_button" class="hidden">cart button</div>

Finding the value of a class within a list

I have
<ul id="list">
<li data-markerid="0" class="">
<div class="list-label">A</div>
<div class="list-details">
<div class="list-content">
<div class="loc-id">2</div>
<div class="loc-addr">England</div>
<div class="loc-dist">2 miles</div>
<div class="loc-addr2">Test</div>
<div class="loc-addr2">Bristol</div>
</div>
</div>
</li>
<li data-markerid="1" class="">
<div class="list-label">A</div>
<div class="list-details">
<div class="list-content">
<div class="loc-id">3</div>
<div class="loc-addr">England</div>
<div class="loc-dist">60 miles</div>
<div class="loc-addr2">Test</div>
<div class="loc-addr2">London</div>
</div>
</div>
</li>
</ul>
I'm wanting to extract the value of this using JQuery.
I tried:
var targetID = $(this).find('.loc-id').text();
But this gets me values of both loc-id's. I want just the one that I'm selecting (clicking).
For full context, please look here: Parsing data using JQuery
$('#list').click(function () {
//Change the src of img
var targetID = $(this).find('.loc-id').text(); // Get the ID
// Since array of objects isn't indexed, need to loop to find the correct one
var foundObject = null;
for (var key in parsedArray) {
if (parsedArray.hasOwnProperty(key) && parsedArray[key].id == targetID) {
foundObject = parsedArray[key];
break;
}
}
// If the object is found, extract the image and set!
if (!foundObject)
return;
var imageSrc = foundObject.LocationPhoto; // From the object
$('#location-image').attr('src', imageSrc); // Set the new source
});
Thanks
In your click handler, this references the <ul> element which has multiple <li> children.
Change the click handler to act as a delegate instead:
$('#list').on('click', 'li', function () {
Now, inside the click handler, this references an <li> element so the search should only yield a single value.
For var targetID = $(this).find('.loc-id').text(); to work, you must be clicking an element that is an ascendant of only one .loc-id. For example:
$('.list-details').on('click',function(){
var targetID = $(this).find('.loc-id').text();
});
You need to change selector. In your event handler. $(this) referes to ul which has multiple loc-id thus when you are using text() its concatenating text.
Use
$('#list li').click(function () {
//Change the src of img
var targetID = $(this).find('.loc-id').text(); // Get the ID
alert('targetID: ' + targetID)
});
instead of
// When we select the item
$('#list').click(function () {
//Change the src of img
var targetID = $(this).find('.loc-id').text(); // Get the ID
DEMO
You could use event.target in case you are delegating on #list:
Demo: http://jsfiddle.net/abhitalks/CxcU8/
$("#list").on("click", function(e) {
var $t = $(e.target);
if ($t.hasClass('loc-id')) {
alert($t.text());
}
});

How to build a Drag and Drop Menu Builder?

Hi all i present i am wokring on Menu Builder,where user can come build his menu how ever he likes with the subemnus to,i have a problem in Drag and Drop, i had done till drag but drop is not working in my case
i have to achevie some thing like this
http://www.prodevtips.com/demos/drag_drop_tree/
but not similar i should be able to create a chain or tree with drag and drop,my scenario is i have all menus listed at the bottom and and add column button at the top,when user wants to build menu he can click on addmenu button and a column opens there he can drag and drop the menus from the listed menu, from here i want the working scenario of the above link i had given where if i drop on any menu item it should be the child of that parent menu item which the user dropped on
here is my script
$(document).ready(function() {
var i = 0;
$("button[id='columnadd']").click(function () {
alert(1);
var domElement = $('<aside id="shoppingCart' + i + '" class="shoppingCart"><h2 class="ui-widget-header">Add Menu Items Here</h2><aside class="ui-widget-content"><ol><li class="placeholder">Add your items here</li></ol></aside></aside>');
i++;
$(this).after(domElement);
});
$(".small_box li" ).draggable({
appendTo: "body",
helper: "clone"
});
});
$(".small_box li a").droppable({
tolerance : "pointer",
hoverClass : "tree_hover",
drop : function(event, ui){
var dropped = ui.draggable;
dropped.css({top: 0, left: 0});
var me = $(this).parent();
if(me == dropped)
return;
var subbranch = $(me).children("ul");
if(subbranch.size() == 0) {
me.find("a").after("<ul></ul>");
subbranch = me.find("ul");
}
var oldParent = dropped.parent();
subbranch.eq(0).append(dropped);
var oldBranches = $("li", oldParent);
if (oldBranches.size() == 0) { $(oldParent).remove(); }
}
});
and here is my html
<body>
<button id="columnadd" >Add Column</button>
<aside class="menu-structer" id="AddColumns" >
</aside>
<aside class="small_box">
<h4>BRANDS</h4>
<ul>
<li id ="brand1"><a class="" href="#">Brand1</a></li>
<li id ="brand2">Brand2</li>
<li id ="brand3">Brand3</li>
<li id ="brand4">Brand4</li>
</ul>
</aside>
<aside class="small_box">
<h4>CATEGORIES</h4>
<ul>
<li id ="category1">Category1</li>
<li id="category2">Category2</li>
<li id="category3">Category3</li>
<li id="category4">Category4</li>
</ul>
</aside>
<aside class="small_box">
<h4>PRODUCTS</h4>
<ul>
<li id="Product1">Product1</li>
<li id="product2">Product2</li>
<li id="product3">Product3</li>
<li id="product4">Product4</li>
</ul>
</aside>
</body>
can any one help me over here please...that would be of a a great help for me..i am struggling with the Drop Event
Mistakes i had done
I had created a DOM Element (which is the drop area) upon click and this element is not in your page until you click the trigger. By doing $('#shoppingCart ol').droppable({...}) in $(document).ready(function() {}) block you try to add droppable to an element that even not in your page, so this won't do a thing.
changes
What i had done is make the element droppable after create that element
$("button[id='columnadd']").click(function () {
// create the element
var domElement = $('<aside id="shoppingCart' + i++ + '" class="shoppingCart"><h2 class="ui-widget-header">Add Menu Items Here</h2><aside class="ui-widget-content" id="droppable"><ol><li class="placeholder">Add your items here</li></ol></aside></aside>');
// put it after $(this)
$(this).after(domElement);
// at this point you have domElement in your page
// make it droppable
domElement.find("ol").droppable({
// put options here
greedy: true,
drop: function (event, ui) {
makeItDroppableToo(event, ui, this);
}
});
});
and this is the function to make your dropped element has its own placeholder
function makeItDroppableToo(e, ui, obj) {
$(obj).find(".placeholder").remove();
var placeholder = $("<ol><li class='placeholder'>You can also drop it here</li></ol>");
$("<li></li>").append(ui.draggable.text()).append(placeholder).appendTo($(obj));
// this is the same as the previous one
placeholder.droppable({
// put options here
greedy: true,
drop: function (event, ui) {
makeItDroppableToo(event, ui, this);
}
});
}

jquery mouseenter fires continuosly

I have a mousenter/mouseleave function that swaps out content when the element is being hovered over. however instead of it firing only on mouseenter it seems to keep continuously firing. I have created a jsfiddle here so hopefully it will be easier to understand.
Inside your rotate_start() and rotate_reset() functions you are triggering a hover event via this line:
jq('.product-image-container',super_prod).attr('hover','true');
Per JQuery docs, hover() method binds handlers for both mouseenter and mouseleave events. You're basically applying recursion unintentionally, as hover is fired as the mouse moves.
A simple fix for this is to replace .attr('hover','true'); with .data('hover',true);
Here is a scalable solution which should accomplish your objective:
HTML:
<div class="item">
<ul>
<li>
Main product photo
</li>
<li>
Alternate product photo 1
</li>
<li>
Alternate product photo 2
</li>
<li>
Alternate product photo 3
</li>
</ul>
</div>
CSS:
.item {
width:200px;
height:200px;
background-color:#F0F0F0;
border:1px solid #666;
}
.item ul li {
display:none;
}
JQuery:
var jq = jQuery.noConflict();
var timers = [];
var interval_seconds = 2;
jq('.item').mouseenter(function() {
// Scope this
var _this = jq(this);
// Self-executing function
(function cycle() {
// Recursive setTimeout accommodates for animation time - setInterval wont
timers['item'] = setTimeout(function() {
// Grab the currently visible photo, and the next photo
var _this_li = _this.find('li:visible');
var _next_li = _this_li.next();
// If there is no next photo, cycle back to first photo
if (_next_li.length == 0) {
_next_li = _this.find('li:first-child');
}
// Animate the rotation
_this_li.fadeOut('slow', function() {
_next_li.fadeIn('slow', function() {
// Recursion
cycle();
});
});
}, interval_seconds * 1000);
})();
});
jq('.item').mouseleave(function() {
// Stop the recursive setTimeout
clearTimeout(timers['item']);
// Scope this
var _this = jq(this);
// Grab the first photo in the list
var _first_li = _this.find('li:first-child');
// If the first photo in the list is not the currently visible photo, make it so.
if (_first_li.not(':visible')) {
_this.find('li:visible').fadeOut('slow', function() {
_first_li.fadeIn('slow');
});
}
});
jq(function() {
// Show the first photo on load
jq('.item').find('li:first-child').fadeIn('slow');
});
Demo: http://jsfiddle.net/AlienWebguy/EY8mM/1/

can this JavaScript function be refactored?

I need to use prototype JavaScript library on a project and wanted to add tabbed box to HTML.
The click handler has a simple task - set selected on parent <li> element and show linked DIV id element (rel tag on <li> has element id name)
<div class="tabInterface">
<ul class="tabSelector">
<li class="selected" rel="searchLast">Popularna iskanja</li>
<li rel="searchMine">Moje zadnje iskanje</li>
</ul>
<div class="tabContent selected" id="searchMine">
box 1 content
</div>
<div class="tabContent" id="searchLast">
box 2 content
</div>
</div>
Final result after 1 hour of hard labour.
initTabInterface = function() {
//requires prototype
var tabClick = function(event){
Event.stop(event);
var $el = Event.element(event);
var $menu = $el.up('.tabSelector');
var liList = $menu.descendants().filter(function(el){return el.match('li')});
liList.invoke('removeClassName', 'selected');
$el.up().addClassName('selected');
var rel = $el.up().readAttribute('rel');
var $interface = $menu.up('.tabInterface');
var tabList = $interface.descendants().filter(function(el){return el.match('.tabContent')});
tabList.invoke('removeClassName', 'selected');
$interface.down('#'+rel).addClassName('selected');
};
$$('.tabInterface .tabSelector li a').each(function(el){
var $el = $(el);
Event.observe($el, 'click', tabClick);
});
};
Event.observe(window,"load", function(){
initTabInterface();
});
Is there an easier way of traversing in prototype than with the bunch of up, down, filter, match, invoke and each?
This is pretty much you can get:
initTabInterface = function() {
//requires prototype
var tabClick = function(event){
Event.stop(event);
var $el = Event.element(event);
var rel = $el.up().readAttribute('rel');
// remove old selected classes
$el.up('.tabInterface').select('.selected')
.invoke('removeClassName', 'selected');
// add new selected classes
[ $(rel), $el.up() ].invoke('addClassName', 'selected');
};
$$('.tabSelector li a').each(function(el){
Event.observe($(el), 'click', tabClick);
});
};
Event.observe(window,"load", function(){
initTabInterface();
});​
I do not know Prototype well enough to quickly refactor your code, but you can use Element.siblings instead of going up and then descending down. Alternatively, you can just enumerate by class names (doesn't work well if you have more than one tab control).
$el.siblings().invoke('removeClassName', 'selected');
Also
$interface.down('#'+rel).addClassName('selected');
is unnecessary because you can only have one element with an id in the whole document. Can change it to:
$(rel).addClassName('selected');

Categories