Building a simple dropdown menu with JS for Wordpress
I was wondering why do I get a different outcome when
function intDropDownMobile() {
$('.menu-item-has-children').click(function () {
// e.preventDefault();
var subMenus = $(this).siblings();
var subMenuItems = $('.menu-mobile .sub-menu');
$(this).children('.menu-mobile .sub-menu').slideToggle(300);
if (subMenus.children('.menu-mobile .sub-menu').is(':visible')); {
subMenus.children('.menu-mobile .sub-menu').hide(300);
}
});
}
and this
// Te submenu
function intDropDownMobile() {
$('.menu-item-has-children').click(function () {
// e.preventDefault();
var subMenus = $(this).siblings();
var subMenuItems = $('.menu-mobile .sub-menu');
$(this).children(subMenuItems).slideToggle(300);
if (subMenus.children(subMenuItems).is(':visible')); {
subMenus.children(subMenuItems).hide(300);
}
});
}
They both work but in the second option acts different and collapse the all menu also the non submenu items, was wondering why this happens,
Thanks
Both look suspect in that selector sub-menu probably should be .sub-menu (prefixed with a .)
In the second example, you're using subMenuItems to filter the children of $(this), but subMenuItems is probably going to always contain no elements because of the issue I pointed out above. Accordingly, $(this).children(subMenuItems). will contain no elements as well.
-- EDIT --
This is from your first example:
var subMenuItems = $('.menu-mobile .sub-menu');
$(this).children('.menu-mobile .sub-menu').slideToggle(300);
I don't think that logically, this selector can work when used with the children() function. Because there is a space in the selector, its results span TWO levels of hierarchy (the elements with class menu-mobile, and their respective sub-elements with class sub-menu). Since the results span two levels, it'd be ambiguous to the function as to which -- the inner, or outer -- to return as 'child'. So none are returned!!!
This is from your second example:
var subMenuItems = $('.menu-mobile .sub-menu');
$(this).children(subMenuItems).slideToggle(300);
In this example, all elements in subMenuItems will have class sub-menu. SOME of them may be children of $(this), and those are returned.
So in the second example, you selected all elements matching $('.menu-mobile .sub-menu') in the document, and filtered to children of $(this). But in the first, you gave a selector that will never work.
Related
I'm using jQuery traversing to jump between DOM elements.
First of i have a onClick function:
$(document).on('keyup', '.size, .ant', function(){
Inside of this function I send data about what's clicked, to another function.
sulorTableRowWeight( $(this) );
function sulorTableRowWeight(thisobj){
Now, I'd like to traverse from the clicked element $(this) to its parent. I'd like to find the parent's siblings and then traverse down to a specific sibling.
var inputSize = $(thisobj).parent().siblings('.sizeTd').children('.size');
My problem is when I want to traverse back down to the element I came from, it is not listed as a sibling because it isn't a sibling...
var inputSize = $(thisobj).parent().siblings(); console.log(inputSize)
console will give me the siblings, but not the one U came from...
So, when a user clicks ".size" I'd like to traverse up to the parent and back to size.... When a user clicks ".ant" I'd like to traverse up to the parent and then down to ".size"...
I tried to hardcode the traversing:
var inputSize = $(thisobj).parent().siblings('.sizeTd').children('.size');
But it won't work because it is not actually a sibling.
So what is it? And how can I get back to it?
If it is not possible, I have to run some if/else statements, U guess...
UPDATE
$(document).on('keyup', '.size, .ant', function(){
//Find changed <select> .tbody
var tbodyId = $(this).parent().parent('tr').parent('tbody').attr('id');
//Check <tbody> #id
if(tbodyId === "cpTableBody"){
}
else if(tbodyId === "sulorTableBody"){
sulorTableRowWeight( $(this) );
}
else if(tbodyId === "konturTableBody"){
konturTableRowWeight( $(this) );
}
else if(tbodyId === "kantbalkTableBody"){
kantbalkTableRowWeight( $(this) );
}
})
//Function sulorTableRowWeight
function sulorTableRowWeight(thisobj){
//Find the selected data-weight
var selectedWeightmm3 = $(thisobj).parent().siblings('.selectTd').children('.select').find(':selected').data('weightmm3');
//Find input .size value
var inputSize = $(thisobj).parent().siblings('.sizeTd').children('.size'); console.log(inputSize)
PROBLEM
My var inputSize will return undefined when I click a ".size" element. That´m's because it is not listed as a sibling to itself.
I know it's keyup, not click...
e.target will select the current input
$(document).on('keyup', '.size, .ant', function(e) {
inputSize = $(e.target);
if($(e.target).is('.ant')) {//test if the element is .ant
inputSize = $(e.target).parent().find('.size');//get .size based on .ant
}
console.log(inputSize[0]);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<input class="size x1" placeholder="x1">
<input class="ant x1" placeholder="x1 ant">
</div>
<div>
<input class="size x2" placeholder="x2">
<input class="ant x2" placeholder="x2 ant">
</div>
Hmm, if you're passing in $(this) as thisObj I don't think you need to be nesting thisObj in a $(). (See note below)
Anyway, you could try using .parents('<grandparent>').find('<child>') so basically you're traversing one higher level up the tree with <grandparent>, then getting all the descendants that match the child selector. That should include the branch of the three that $(this) represents. But it's hard to say for sure without seeing your HTML.
** A good practice when assigning jQuery objects to variables is to use $ syntax, ie var $this = $(this) so you know anything prepended with a $ is a jQuery object.
inside sulorTableRowWeight , you should have the reference to the clicked element in thisobj variable.
Im using a table and rows can have child rows and it can go down a few levels,
what is happening now is that when hiding a child element it then opens that childs child element.
Heres my jQuery:
$(document).ready(function() {
function getChildren($row) {
var children = [], level = $row.attr('data-level');
while($row.next().attr('data-level') > level) {
children.push($row.next());
$row = $row.next();
}
return children;
}
$('.parent').on('click', function() {
var children = getChildren($(this));
$.each(children, function() {
$(this).toggle();
})
});
$(".parent a").click(function(e) {
e.stopPropagation();
})
})
I have set up a jsfiddle so you can see whats happening
https://jsfiddle.net/rhvye8k0/4/
If you click the first "+" you will see what im trying to describe.
Cant think how to sort it out
Update,
have sorted it and updated jsfiddle https://jsfiddle.net/rhvye8k0/5/
There may be a way to reduce the jQuery but it works for now
Your problem is the $(this).toggle(); in .parent's onclick handler. The tr at level 3 has style="display:none", the others don't. toggle() will toggle the receiving element(s) visibility so the others are show (their display is implicitly block) and level 3's is hidden.
I am using the Javascript below to animate an accordion (it's a slightly modified variant of the one explained here: http://tympanus.net/codrops/2010/04/26/elegant-accordion-with-jquery-and-css3/.
Now I wanted to have the first element to be open on pageload, so I figured I just give it some sort of extra-class via Javascript (and define that .active state via CSS) to have it open up.
This worked, however if I hover over any but the first-element with said .active class, the first element keeps its state, and stays open until I hover over it at least once.
So, what I want is: the first element of my accordion is open and collapses if the user hovers over any of the elements that are not the first. I think I need to add a line in the hover function to either take the class away of the first element or to give the new element the active state, but I don't know how to do it and keep breaking the thing.
<script type="text/javascript">
jQuery(function() {
activeItem = jQuery("#accordion li:first");
jQuery(activeItem).addClass('active');
jQuery('#accordion > li, #accordion > li.heading').hover(
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
},
function () {
var jQuerythis = jQuery(this);
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
}
);
});
</script>
Looks like this is happening because each accordion item has its own hover event that takes care of its own animation. You can refactor the code slightly to make this easier to understand and reuse:
var activeItem = jQuery("#accordion li:first");
jQuery('#accordion > li, #accordion > li.heading').hover(
function () { hoverMe(jQuery(this)); },
function () { unhoverMe(jQuery(this)); }
);
//This gets called when cursor hovers over any accordion item
var hoverMe = function(jQuerythis) {
//If the first item is still active
if (activeItem) {
contract(activeItem); //...Shrink it!
activeItem = false;
}
//Expand the accordion item
expand(jQuerythis);
};
//This gets called when cursor moves out of accordion item
var unhoverMe = function(jQuerythis) {
contract(jQuerythis);
};
//I have moved the hover animation out into a separate function, so we can call it on page load
var expand = function(jQuerythis) {
jQuerythis.stop().animate({'height':'280px'},500);
jQuery('.heading',jQuerythis).stop(true,true).fadeOut();
jQuery('.bgDescription',jQuerythis).stop(true,true).slideDown(500);
jQuery('.description',jQuerythis).stop(true,true).fadeIn();
};
//I have moved the unhover animation out into a separate function, so we can contract the first active item from hoverMe()
var contract = function() {
jQuerythis.stop().animate({'height':'40px'},1000);
jQuery('.heading',jQuerythis).stop(true,true).fadeIn();
jQuery('.description',jQuerythis).stop(true,true).fadeOut(500);
jQuery('.bgDescription',jQuerythis).stop(true,true).slideUp(700);
};
//Now expand the first item
expand(activeItem);
I have put together a simplified version demonstrating the logic. Please let me know how you get on.
I am trying to toggle via a select option. I am having difficulties toggling more than two. My goal is to be able toggle as far as 4 through the select option. For example Categories and subcategories. Here is my example in jsfiddle.
<script type="text/javascript">
var op = $("#tables option[value='options']:selected");
var os = $("#tables option[value='Example2']:selected");
if (op.length)
$("#something").show();
else
$("#something").hide();
if (op == ("#something").show())
$("#something2").show();
else
$("#something2").hide();
}
</script>
Is this the design pattern you are looking for?
if your object is not selected
if your parent is selected
you are also selected
else
you are selected
else
you are now unselected
This logic will work for any depth of recursion.
EDIT: Assuming each menu is a ul. You'll have to tweak the selectors.
This is just one way to do it. Not the best if you have events firing on visibility, or if you have ui reflow issues.
clickyclicky = function(event) {
var $target = $(event.currentTarget);
if (!$target.hasClass("selected")) {
// hide the old target and its parents
var $oldTarget = $('.selected');
$oldTarget.removeClass("selected").hide().parents('ul').hide();
// show the new target and its parents
$target.show().addClass("selected").parents('ul').show();
} else {
// hide the target
$target.removeClass("selected").hide();
// move the selected token to the parent.
$parent = $target.parent().parent(); // assuming an ul/li tree pattern.
if ($parent.is('ul')) {
$parent.addClass("selected");
}
}
}
I haven't tested this code, it's just a general reference.
EDIT: Here's the Fiddle: http://jsfiddle.net/uA7XD/76/
UPDATE 2:
Thanks so much for all your help. While all three solutions worked, I like Bill's in terms of readability and performance. As always, I'm amazed by the level of expertise and help here. REALLY appreciate the help.
UPDATE:
Put demo up on jsfiddle: http://jsfiddle.net/FC4QE/17/
I need to create a filter. Users click on a brand name link and if there is a match then I need to filter out the other products. The brand is contained in a product name, so I'm searching for a match and if there is one or many, I need to hide the other products.
I have the following javascipt/jquery code:
$(function(){
$('#filter-by-brand li a').click(function(){
// get html string of clicked item
var brandNameSelected = $(this).html();
var productContainer = $('#product-collection div.productBoxWrapper');
// reset products in the view
if (brandNameSelected == 'All Brands'){
productContainer.fadeIn("slow");
}
// for each product title (a tag)
$("#product-collection h4 a").each(function(){
var productTitle = jQuery(this).html();
// if item clicked is contained inside product title, hide all
// products and only show the ones where the title matched
if(productTitle.indexOf(brandNameSelected) != -1){
// hide container of all products, this hides all products
productContainer.fadeOut("slow");
// then show only ones that match. the problem is that only the one product is
// displayed since we're inside the .each. How can I show all products where product title contains the item clicked?
$(this).parent().parent().parent().parent().fadeIn("slow");
}
});
});
});
I explained everything in the comments inside the code, but basically, while the code works, because I'm showing the products where item clicked is contained inside the .each method, it only shows the last item matched. How can I show all the matched ones inside the .each or is this impossible and is there another way?
Hope this makes sense and that someone might have some advice!
Thanks.
I got the nicest looking results from this:
$('#filter-by-brand li a').click(function()
{
var brandNameSelected = $(this).html();
var productContainer = $('#product-collection .product-container');
if (brandNameSelected == 'All Brands')
{
productContainer.fadeIn("slow");
}
else {
$(".product-container")
.fadeOut(100)
.delay(100)
.filter(function() {
return $(this).html().indexOf(brandNameSelected) > -1;
})
.each(function(index, item) {
$(item).fadeIn("slow");
});
}
});
You can play with it at http://jsfiddle.net/tu8tc/1/;
For "all brands", bail out. For specific brand names, hide all productContainers unconditionally then selectively fadeIn those that meet the criterion.
$(function() {
$('#filter-by-brand li a').click(function() {
var brandNameSelected = $(this).html();
var productContainer = $('#product-collection .product-container');
if (brandNameSelected == 'All Brands') {
productContainer.fadeIn("slow");
return;
}
productContainer.hide();
$("#product-collection h4 a").each(function() {
var productTitle = $(this).html();
if(productTitle.indexOf(brandNameSelected) != -1) {
$(this).closest(".product-container").stop().fadeIn("slow");
}
});
});
});
See update of your fiddle
Note how jQuery's .closest() avoids the ugly .parent().parent().parent().
.stop() is precautionary, just in case a fadeout() is already running on the element. Not necessary if this is the only code that animates productContainers.
EDIT...
Or to be concise and more efficient, with judicious use of jQuery's .filter you can do almost everything in one statement (though readability suffers):
$(function() {
$('#filter-by-brand li a').click(function() {
var brandNameSelected = $(this).html();
$('#product-collection').find('.product-container').hide().end().find('h4 a').filter(function() {
return (brandNameSelected == 'All Brands') || ($(this).html().indexOf(brandNameSelected) != -1);
}).closest(".product-container").stop().fadeIn("slow");
});
});
See further update to fiddle
Why not simply hide products that you want filtered out?
if(productTitle.indexOf(brandNameSelected) == -1)
{
$(this).parent().parent().parent().fadeOut("slow");
}
See http://jsfiddle.net/2Sduq/1/ .