What is wrong with my jQuery code? - javascript

I want to toggle a div from left to right when someone clicks on the h1 which is floating on right every thing is working just fine but I can't toggle it. Once the div appears it never goes away.
<script>
$(document).ready( function (){
/*
THIS COMMENTED SECTION IS WORKING BUT WITHOUT TOGGLE
$('h1').click(function (e){
e.preventDefault();
$('div').animate({
'left': 0
}, 'slow');
});
*/
// This is not working!
$("h1").click(function(){
$("div").toggle(function() {
$("div").animate({'left': '-32%'});
}, function() {
$("div").animate({height: '0'});
});
});
});
</script>
And the HTML:
<h1>Click here!</h1>
<div style="left: -32%; display: block;">Put whatever content you want here!
<ul>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
</ul>
</div>

The multiple function argument form of .toggle() has been deprecated in jQuery 1.8 and removed in 1.9 and it had a built in click handler. So, it didn't work the way you are trying to use it and it is no longer supported.
Here's a replacement for the multi-function version of .toggle():
jQuery.fn.cycleFunctions = function() {
var args = Array.prototype.slice.call(arguments);
// initialize index into function arguments
var indexTag = "_cycleIndex";
this.data(indexTag, 0);
this.on("click", function(e) {
var self = $(this);
var index = self.data(indexTag) % args.length;
self.data(indexTag, index + 1);
return args[index].call(this, e);
});
return this;
};
Note, this already does the .click() handler for you so you don't put this inside a click handler.
So, combined with your code:
<h1>Click here!</h1>
<div class="toggle" style="left: -32%; display: block;">Put whatever content you want here!
<ul>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
<li>CLICK ON THE LINK</li>
</ul>
</div>
// This works with two operations that are opposites of each other
// which your two animations are not
$("h1").cycleFunctions(function(){
$(this).next(".toggle").slideUp();
}, function() {
$(this).next(".toggle").slideDown();
});
You would then have to fix your CSS to that the two actions are reversible. In order to move with the left style, the item has to have a non-default position style setting. And, once the second click happens and the item has a zero height, the item will never be visible again so this could operate two clicks and never be seen again.
Here's a working demo: http://jsfiddle.net/jfriend00/Xj65L/

Take a look at the jquery documentation: http://api.jquery.com/toggle/
.toggle( [duration ] [, complete ] )
The first parameter is is not a function.

Related

Hide siblings when another dropdown is opened

I'm fairly new to .js and have been working on a dropdown nav menu. I've got most of it functioning, but I was asked to include a specific snippet for the menu activation.
I'd like to figure out how to make the other subnav items hide or scroll up when a different subnav is opened.
What am I doing wrong here?
<div id="nav_mob">
<div id="nav-toggle"><span></span></div>
<div class="dropdown_mob">
<ul>
<a class="dropdown_btn">
<li>Overview</li>
</a>
<div class="subnav_mob">
<ul>
<li>Introduction</li>
<li>Research</li>
<li class="padded">Planning & Preparation</li>
<li>International</li>
</ul>
</div>
<a class="dropdown_btn"><li>Profile</li></a>
<div class="subnav_mob">
<ul>
<li>My Account</li>
<li>My Cart</li>
<li>Check Out</li>
<li>Log Out</li>
</ul>
</div>
<a class="dropdown_btn"><li>Search</li></a>
<div class="subnav_mob">
<ul>
<li><div id="smallsearch"><input type="text"></div></li>
</ul>
</div>
</div>
</ul>
</div>
</div>
the snippet I was given:
var dropdown = document.getElementsByClassName('dropdown_btn');
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener('click', function() {
this.classList.toggle('active');
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display == 'block') {
dropdownContent.style.display = 'none';
} else {
dropdownContent.style.display = 'block';
}
});
}
and the fix I tried to implement:
$(document).ready(function() {
$('.dropdown_btn').on('click', function() {
var state = $('.dropdown_btn').is('.active');
if(state) {
$('.dropdown_btn').removeClass('active').next('.subnav_mob')
.slideUp();
} else {
$('.dropdown_btn').addClass('active').next('.subnav_mob').slideDown();
$.closest('.dropdown_btn').siblings('.dropdown_btn')
.find('.dropdown_mob').slideUp().end();
$.find('.dropdown_btn').not(this).removeClass('active');
}
})
})
Your first problem is that $('dropdown_button') selects every element with that same class, not just the one you clicked on. Operating on it will thus operate on every dropdown at once. You may have noticed that clicking one button causes every dropdown to open, and clicking another button causes them all to close again. This is why.
Your second problem is that $.closest is not a thing. If you press F12 and check out the console, you'll notice an error being thrown from that line, saying that '$.closest' is not a function. It's actually 'undefined', and attempting to invoke it as a function with () causes this error. This prevents any code after this point from being run, though even if you fix this that code still won't work for similar reasons. $.find is not a function, either, for example. closest and find, like next and slideup, are methods on jQuery instances, not on the global jQuery object itself.
This should work. Note that $(this) refers to the clicked element wrapped in a JQuery instance:
$(document).ready(function() {
$('.dropdown_btn').on('click', function() {
var state = $(this).is('active');
if(state) {
$(this).removeClass('active')
.next('.subnav_mob').slideUp();
} else {
$(this).addClass('active')
.next('.subnav_mob').slideDown();
$(this).siblings('.dropdown_btn').removeClass('active')
.next('.subnav_mob').slideUp();
}
})
})
I would recommend stepping through each call in this, compare it with the jQuery documentation, to really make sure you understand it. I'd also might recommend trying to do it without jQuery-- using the native DOM API like the original snippet was doing. Such an exercise might be frustrating, but valuable.

Jquery Scroll to elements without Ids, only by data attributes

I want the page to scroll down to my "container"-li which wraps my current elements. The li doesn't have an id attribute and it can't have for HTML validation purposes. Each li has a data attribute named: data-commentid This attribute contains a GUID and is what i want to use when binding the scroll.
How do i scroll to a element only by using it's data-attribute?
var container = $("body").find(".roomWall.commentBox.publicListing:first");
var targets = $(container).find("li.commentInList.topLevelComment");
if ($.cookie('scrollTarget')) {
var cookieValue = $.cookie("scrollTarget").split('"');
var splittedStr = cookieValue[1];
$(targets).each(function () {
var $self = $(this);
if ($($self).attr("data-commentid") === splittedStr) {
var target = $("#" + splittedStr);
$($self).fadeOut("slow").hide().delay(500).queue(function (next) {
next();
}).delay(1000).queue(function (next) {
var $container = $("html,body");
$($container).animate({
scrollTop: $self.offset().top
}, 1000);
next();
}).delay(1000).queue(function () {
$($self).fadeIn(1000);
$(target).focus();
});
$.removeCookie("scrollTarget", { path: "/" });
}
});
}
The cookie contains the GUID I want. Inside my li ($self) there's a textarea with id={Some Guid} which, in this case refers to my $target variable. Since I didn't managed to get it working with $self.offset().top I added $scrollTo and tried with that one, which didn't work either.
Regarding to the Markup, It's pretty complex so I will only show you the parts we need, to prevent confusion. A wrapping li with the data-commentId:
Further down the hierarchy we have:
Hope someone can find out where my code breaks.
I would recommend using jQuery's filter() instead of trying to concatenate it in. I also think the $("html,body") part is where it's bugging out for you. Otherwise your code is actually correct:
var someGuid = "0003";
var $scrollTo = $("li.commentInList.topLevelComment").filter(function(i){
return $(this).data('commentid') === someGuid;
});
$(document.body).animate({
scrollTop: $scrollTo.offset().top
}, 1000);
li {
height: 10em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="commentInList topLevelComment" data-commentid="0001">Comment 1</li>
<li class="commentInList topLevelComment" data-commentid="0002">Comment 2</li>
<li class="commentInList topLevelComment" data-commentid="0003">Comment 3</li>
<li class="commentInList topLevelComment" data-commentid="0004">Comment 4</li>
<li class="commentInList topLevelComment" data-commentid="0005">Comment 5</li>
</ul>
How was it supposed to scrolldown to something that's faded out? Removed the fading effects and it ( scrollTop: $self.offset().top) worked like a charm.

.child() returns 2nd level items

Well i think .child() function is my problem... But im not sure about this.
I have this in html
<div class="tabs" data-name="1st level tabs">
<ul class="nav">
<li>1st level link</li>
<li>1st level link</li>
</ul>
<ul class="content">
<li>
1st TEXT
<div class="tabs" data-name="2st level tabs">
<ul class="nav">
<li>2nd level link</li>
<li>2nd level link</li>
</ul>
<ul class="content">
</ul>
</div>
</li>
<li>1st TEXT</li>
</ul>
</div>
One element with class "tabs" inside another... ok; in JS:
$(function($) {
var Tabs = function(element,options){
self = this;
self.$element = element;
self.testdrive = function (){
console.log(self.$element.attr("data-name"));
}
self.$element.children(".nav").children("li").children("a").click(function(e){
e.preventDefault();
//Returns EVER 2nd level
self.testdrive();
//Triggering directly a Tabs instance returns EVER 2nd level
$(this).closest(".tabs").data("test.tabs").testdrive();
});
}
//JQuery plugin
$.fn.tabs = function () {
return this.each(function () {
var $this = $(this);
var data = $this.data('test.tabs');
//Creating only one instance of Tabs
if(!data){
data = new Tabs($this);
$this.data('test.tabs',data);
}
});
}
//Adding tabs plugin to all ".tabs"
$(function() {
$('.tabs').each(function(){
$(this).tabs();
});
});
}( jQuery ));
When $(element).tabs() called, creates a instance of function Tabs inside a data attribute test.tabs. If test.tabs has been defined only uses an old instance to preserve it.
To test it, i created a function called testdrive, to print in console "data-name" attribute when .tabs>.nav>li>a has been clicked.
The code works but, in console i receive "2st level tabs" if i clicked on 1st level item.
Is a problem with child() function? Something wrong in my code?
Thanks for help.
The problem is the variable declaration for self, since you haven't used var to declare it is created in the global context. So
var self = this;
also you are setting the data using key test.tabs, but is reading it using key simple.tabs
$(this).closest(".tabs").data("test.tabs").testdrive();
Demo: Fiddle
Also I think you can use self.$element.find(" > .nav > li > a").click(function (e) {}); to register the click handler

How do I attach a single function to a multiple element?

If I have two anchor tags that would trigger a same function, (i.e. multiple button to close a navigation bar) how would I bind both elements with the event? No JQUERY
html:
Close Nav
<ul id="nav">
<li>Nav One</li>
<li>Nav Two</li>
<li>Nav Three</li>
</ul>
Close Nav
JS:
var ctlOne = document.getElementById("ctl_one");
var ctlTwo = document.getElementById("ctl_two");
var both = ctlOne || ctlTwo //<-- this is what I tried and failed.
both.onClick = function(){
toggleNav();
}
function toggleNav(){
// either close or open nav <UL>
}
Again, I know how to do it in jQuery. I'm looking for a native javascript method.
var ctlOne = document.getElementById("ctl_one");
var ctlTwo = document.getElementById("ctl_two");
ctlOne.onClick = ctlTwo.onclick = toggleNav;
You can't do it in one line, but I don't see what's stopping you from doing this:
ctlOne.onClick = toggleNav;
ctlTwo.onClick = toggleNav;
Or, more DRY:
var both = [ctlOne, ctlTwo];
for (var i=0; i<both.length; i++) {
both[i].onClick = toggleNav;
}
REAL ONE eventHandler on multiple items
When i create lists or grids which need the same function i use only one eventhandler
and decide inside that handler what to do.
Heaving one eventhandler means if you unload the code you have to remove only one handler, but also uses alot less resources and updating the code is simpler.
In your case i had to include a long function (slice call...) to get the index of the clicked li
and check if i should execute your toggleNav function or not.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script>
(function(W){
var D;
function init(){
D=W.document;
D.getElementById('ul').addEventListener('click',h,false);
}
function h(e){
var a=e.target,i=Array.prototype.slice.call(a.parentNode.childNodes).indexOf(a);
if(a.parentNode.id=='ul'){
console.log('text: ',a.textContent,' index: ',i,' execute toggleNav: ',i<2);
}
}
W.addEventListener('load',init,false)
})(window)
</script>
</head>
<body>
<ul id="ul"><li>a</li><li>b</li><li>c</li></ul>
</body>
</html>
As you can see the code is short, no loops. But most of all you only have ONE handler!!
this naturally works on modern browsers only but with some changes it does the same in all browsers, i think something like e=e||window.event; e.target=e.target||e.srcElement..
EDIT
SIMPLE EXAMPLE WITH LONG VARIABLE NAMES and WHITESPACES everywhere as requested by matt,
but use the above example at it caches the various elements and it's compatible with external libraries even if you extend it.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script>
function inizializzation () { // Here i inizialize the eventhandler
var TheULElement=window.document.getElementById('ul'); // search
// for the element which id is ul
TheULElement.addEventListener ( 'click' , clickhandler , false )
// here i add an event handler to the element which id is ul
}
function clickhandler ( clickevent ) { // that handler i previously added
if ( clickevent.target.parentNode.id == 'ul' ) { // here i check if the
// parent node id of the element i clicked is ul
console.log( clickevent.target.textContent );
// this writes to the console
//-> rigthclick 'inspect element' (in chrome)
}
}
window.addEventListener ( 'load' , inizializzation , false )
// this is the event handler when the page loads
// which starts the first function called inizializzation
</script>
</head>
<body>
<ul id="ul">
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
<li>f</li>
<li>g</li>
<li>h</li>
<li>i</li>
</ul>
</body>
</html>
The point of this answer is not just to handle the 2 elements but multiple elements
and that reflets the titleof the question
How do I attach a single function to a multiple element?
or also multiple elements which contains multiple elements.
in that case you need to think like on a large grid
rows are the li's
inside this li's i could add buttons spans or whatever and again only with that single eventhandler control them all.
based on the row/colum number or whatever you immagination has to offer.
with cols/rows your handler is on the prantNode of the parentNode.
if ( clickevent.target.parentNode.parentNode.id == 'ul' ) {
console.log( clickevent.target.textContent );
}
and the rows/colums would be
<ul> // whatever (div span) it just has to be a element
<li> // whatever ,best if display:block/flex on all thiselements but
<whatever></whatever> // but with WHITESPACES this does not work
<whatever></whatever> // so it's better you add them with a script
<whatever></whatever> // or learn to write without WHITESPACES
<whatever></whatever> // or new lines.
<whatever></whatever> // because a textnode/newline/whitespace
</li> // in dom is a ELEMENT
<li>
<whatever></whatever>
<whatever></whatever>
<whatever></whatever>
<whatever></whatever>
<whatever></whatever>
</li>
</ul>
Are there LOOPS ? NO.
ps.: i answered this question as it has already the green flag so i just try to help but my laguage is not english so feel free to correct my text . but pls don't touch my code.

detecting the last list item in jquery/javascript

I have a set of tab (main-tabs) on a website and each tab has another set of tabs (sub-tabs).
I want to use arrow keys on a keyboard to navigate the tabs, instead of a mouse.
These tabs are just HTML list items <li>
When I reach the last sub-tab with the arrow key, I want it to go back to the next main tab so it can display its own sub-tabs, and carry on the navigation inside it.
My question is, how can I detect, in jQuery/javascript, when I've reached the last list item (tab) using the arrow keys i.e. the right arrow key?
Many Thanks
You might be able to use either the :last or :last-child selectors in jQuery. Depending on how your <li> tags are nested, you might also have to use the children() function along with it.
For example, let's say you have the following markup:
<ul id="nav">
<li>List item</li>
<li>List item with sub items
<ul>
<li>Sub list item</li>
</ul>
</li>
</ul>
This would select the last top-level <li>
$('ul#nav > li:last').css('border', '1px solid red');
This would select the last <li> traversing the DOM downward. In this case it's the <li> with text "Sub list item"
$('ul#nav li:last').css('border', '1px solid red');
This would select any <li> tags that are the last child of their parent
$('ul#nav li:last-child').css('border', '1px solid red');
var maintabs = $('.maintab'),
active_maintab_eq = 0,
active_subtab_number = 1;
$(document).keyup( function(e){
if (e.which == 39) {
// right arrow key pressed
if ( active_subtab_number == maintabs.eq(active_maintab_eq).find('li').length ) {
// go to next main-tab
// and reset active sub-tab counter
++active_maintab_eq;
active_subtab_number = 1;
} else {
++active_subtab_number;
}
}
});
Some thing like this, I guess.
You can use .length to find out if a jQuery selector found anything:
var nextSubTab = $(currentSubTab).next("li");
if (nextSubTab.length == 0) {
// oops, end of this tab, switch to next tab
}

Categories

Resources