I have a big ul list. Like this:
<ul class="eerstelaag">
<li class=""><a title="?" href="/">? (14)</a>
<ul class="tweedelaag" style="display: none;">
<li>Roerstaafjes</li>
<li>Thee favorieten</li>
<li>Siropen</li>
</ul>
</li>
<li class=""><a title="?" href="/">? (14)</a>
<ul class="tweedelaag" style="display: none;">
<li>Roerstaafjes</li>
<li>Thee favorieten</li>
<li>Siropen</li>
</ul>
</li>
<li class=""><a title="?" href="/">? (14)</a>
<ul class="tweedelaag" style="display: none;">
<li>Roerstaafjes</li>
<li>Thee favorieten</li>
<li>Siropen</li>
</ul>
</li>
</ul>
And i have this javascript:
$(function()
{
var button = $("#assortiment ul.eerstelaag > li");
button.hover(function()
{
if( button.hasClass("open"))
{
var menuitem = $(this).find("ul");
menuitem.slideUp(600);
button.removeClass("open");
}
else
{
var menuitem = $(this).find("ul");
menuitem.slideDown(600);
button.addClass("open");
}
});
});
When i hover over the first li item. Then the ul in that li is coming and show. But now come the problem. When i going fast hover over the li items. The effect i going crazy. And when i going hover faster and faster. It is a crazy effect.
How can i fix this problem?
You're probably looking for the stop function, which will cancel any currently-running animations on the element. You'd use this on the siblings of the element you're about to animate.
Update: Re my comment below, it looks like avoiding chaotic behavior is much trickier than simply stopping the animation. Here's a rough take:
jQuery(function($) {
var button = $("#assortiment ul.eerstelaag > li"),
opening = null,
closing = null;
button.mouseover(function() {
var $this = $(this),
open;
if (!$this.hasClass('open')) {
// If there's an active close operation, cut it short
if (closing) {
closing.stop().css("height", "").hide();
closing = null;
}
// If there's an active opening operation, turn it into
// a closing operation
if (opening) {
closing = opening;
opening = null;
closing.stop().css("height", "").slideUp(600, clearClosing);
}
// Is any sibling open? If so, unmark it it...
open = $this.siblings('li.open');
open.removeClass('open');
if (!closing) {
// ...and since the sibling wasn't still actively
// opening (that's handled above), close it
closing = open.find('ul:first');
closing.stop().css("height", "").slideUp(600, clearClosing);
}
// Open
$this.addClass('open');
opening = $this.find('ul:first');
opening.slideDown(600, clearOpening);
}
});
// Callback used when we're done closing, to clear
// our tracker
function clearClosing() {
if (closing && closing[0] === this) {
closing = null;
}
}
// Callback used when we're done opening, to clear
// our tracker
function clearOpening() {
if (opening && opening[0] === this) {
opening = null;
}
}
});
Live example
Note that when stopping the animation, I had to explicitly clear the height because it could get left in an intermediate state by slideUp / slideDown. You could probably use classes for tracking the actively opening/closing items, rather than the vars I used.
I should mention that in the above I went with putting the "open" class on the li containing the ul, whereas in your original, the "open" class was on the ul itself. If it was purely a marker (you weren't using it in your stylesheets), great; if not, I expect you could modify the above to go back to using the "open" class on the ul, or update the stylesheets to use li.open > ul selector instead of the ul.open selector to style them.
Related
I'm trying to figure out a way to create an extremely basic autocomplete without 3rd parties dependencies. so far I've gotten to populate a list of results with an ajax call, and with mouse onclick events on each li the script completes the fields as supposed.
what I need to implement is an up/down/enter keys navigation system based on pure js, and after hours spent searching I gave up. this fiddle explains quite perfectly my situation, with the difference that it does require jQuery.
I'd rather not paste any of my own code here as the final aim is learning the process, but since I'm linking to jsfiddle I'm required to so here's the fiddle.
fiddle HTML:
<div id="MainMenu">
<ul>
<li class="active">PATIENT TEST</li>
<li>QC TEST</li>
<li>REVIEW RESULTS</li>
<li>OTHER</li>
</ul>
</div>
Up
Down
fiddle JS:
$(document).ready(function () {
$('#btnDown').click(function () {
var $current = $('#MainMenu ul li.active');
if ($current.next().length > 0) {
$('#MainMenu ul li').removeClass('active');
$current.next().addClass('active');
}
});
$('#btnUp').click(function () {
var $current = $('#MainMenu ul li.active');
if ($current.prev().length > 0) {
$('#MainMenu ul li').removeClass('active');
$current.prev().addClass('active');
}
});
$(window).keyup(function (e) {
var $current = $('#MainMenu ul li.active');
var $next;
if (e.keyCode == 38) {
$next = $current.prev();
} else if (e.keyCode == 40) {
$next = $current.next();
}
if ($next.length > 0) {
$('#MainMenu ul li').removeClass('active');
$next.addClass('active');
}
});
});
thanks a lot in advance to anyone willing to point me in the right direction.
This turned out to be simpler than I expected, and I've came up with the following code which appearently does the job quite well.
Things to take in account are:
the HTML attribute 'tabindex' must be specified on each element for the .focus() to be applied
to have a ENTER->submit feeling, you MUST target a link element within the li (still, I'm achieving this with onclick events not included here)
this works with an extremely simple list structure, so far I haven't tested it with nested dropdown menus
Note: this is most likely not suitable for a copy/paste situation, but as far as I can tell this method is procedurally currect, and can get you started developing more complex solutions
This is the basic HTML:
<input type="text" name="main_input" id="input" />
<ul id="list">
<li class="listElement">li content</li>
<li class="listElement">li content</li>
<li class="listElement">li content</li>
</ul>
And here's the JS function, triggered when the list above is populated and shown:
function scrollList() {
var list = document.getElementById('list'); // targets the <ul>
var first = list.firstChild; // targets the first <li>
var maininput = document.getElementById('input'); // targets the input, which triggers the functions populating the list
document.onkeydown = function(e) { // listen to keyboard events
switch (e.keyCode) {
case 38: // if the UP key is pressed
if (document.activeElement == (maininput || first)) { break; } // stop the script if the focus is on the input or first element
else { document.activeElement.parentNode.previousSibling.firstChild.focus(); } // select the element before the current, and focus it
break;
case 40: // if the DOWN key is pressed
if (document.activeElement == maininput) { first.firstChild.focus(); } // if the currently focused element is the main input --> focus the first <li>
else { document.activeElement.parentNode.nextSibling.firstChild.focus(); } // target the currently focused element -> <a>, go up a node -> <li>, select the next node, go down a node and focus it
break;
}
}
}
Apologies in advance for the kinda chaotic layout of the code, the function I came up with is a bit more complex and I've stripped out most of it for explaination purposes.
Needless to say, I'm looking forward any comment about the solution above, in regard of errors, improvements or known compatibility issues.
you wrote
tabindex="1"
Why not just tabindex="0"?
Using a positive umber greater than 0 is only needed if you want to cahnge the default order of navigation.
My website is a parallax one page scrolling website, so all my nav links are directed to ids nested within that page...
For example:
<ul class="clearfix">
<li>Home</li>
<li>Work</li>
<li>About</li>
<li>Contact</li>
</ul>
So How would I tell my html that when someone clicks on one of these links and directs them to the corresponding ID on the page, to take on the class active? And the link that was active to turn back to the regular styling?
Assuming your link elements are contained in an element with class nav, and you're using jQuery, you could do the following:
$('.nav a').on('click', function(event) {
$('.nav a.active').removeClass('active');
$(this).addClass('active');
});
fiddle
You will have to use JavaScript to add that functionality into your application. Everytime a link is clicked, add the 'active' class to the triggering element, and remove it from all others. This is straightforward if you can use jQuery (jsFiddle with jQuery), and only a little more tedious otherwise.
$(function() {
$("ul.clearfix > li > a").click(function() {
$("a.active").removeClass("active");
$(this).addClass("active");
});
});
If you're only using native JS, you can try something along the lines of the below (jsFiddle using vanilla JS):
var links = document.getElementsByTagName("a"); // more specific selector if other links
for (var i = 0; i < links.length; i++) {
var link = links[i];
link.onclick = function () {
var prev = document.getElementsByClassName("active");
if (prev && prev[0]) {
prev[0].className = ""; // if using other classes, filter better
}
this.className += " active";
};
}
This second solution needs to be adapted to fit your particular application/DOM structure, since it's not quite as flexible as the first.
jQuery
$('ul a').on('click', function(event) {
$('a').removeClass("active");
$(this).addClass("active");
});
Replace ul a with something more specific like .nav a
I currently have a list of <li>'s. Each <li> will have a color class defined, example: .color-blue, .color-red, .color-green - like so:
<ul id="listings">
<li class="layer block color-blue" id="item-1"></li>
<li class="layer block color-red" id="item-2"></li>
<li class="layer block color-green" id="item-3"></li>
</ul>
How do I copy/get the color class of the specific <li> item that is clicked?
I have my click listener in place and also know how to get the <li "id"> however not sure on the specific class though.
/* Click listener */
document.getElementById("listings").addEventListener("click", function(e) {
//console.log(e.target.id + " was clicked");
});
Something like this:
document.getElementById("listings").addEventListener("click", function(e) {
var el = e.target;
if (el.tagName == "LI") { // Use only li tags
for (i=0; i < el.classList.length; i++) {
if (~el.classList[i].indexOf('color')) {
var color = el.classList[i];
console.log('color class found: '+color);
break;
}
}
}
});
http://jsfiddle.net/bHJ3n/
You can use (jQuery):
$('ul').find('li.layer block color-blue')
Or
$('ul#listings').find('li.layer block color-blue')
Or... you can not use jQuery as that wasn't in the original question and would be wasteful to include unnecessarily.
Here's a solution that works in vanilla JS:
jsFiddle Example
Essentially because you're lumping the colour among the other classes you have to split them into an array and iterate over them until you find the one that starts 'color-'. I would recommend you use a custom attribute instead, like data-color="blue" as that would mean you could just retrieve it with:
e.target.getAttribute('data-color');
Try
document.getElementById("listings").addEventListener("click", function(e) {
alert(e.srcElement.className);
});
DEMO
UPDATE(since it is not working in Firefox as pointed from Sai):
To work also in Firefox try this:
document.getElementById("listings").addEventListener("click", function(e) {
var target = e.target || e.srcElement;
alert(target.className);
});
DEMO2
I've read many tutorials and can't seem to get it right. Ok I know that the jquery click function works when you are doing something to the exact same element but how do you make it effect another and toggle back?
I have a menu and when I click on an item I want the background (body) to change to an image.
Example:
HTML
<body>
<ul class="menu">
<li class="menu-item"><a>item 1</a></li>
<li class="menu-item"><a>item 2</a></li>
<li class="menu-item"><a>item 3</a></li>
</ul>
</body>
JQUERY
$(".menu-item a").click(function () {
$(body).css('background', 'http://example.com/image.png'); <-- first menu item
$(body).css('background', 'http://example.com/image.png'); <-- second menu item
$(body).css('background', 'http://example.com/image.png'); <-- third menu item
});
You can use .index() - DEMO
$("a").on("click", function(e) {
e.preventDefault();
var i = $("li").index( $(this).parent() );
if ( i === 1 ) {
$('body').css('background', 'beige');
} else if ( i === 2 ) {
$('body').css('background', 'honeydew');
} else {
$('body').css('background', 'pink');
}
});
Does this seem about like what you're trying to do?
$(".menu-item a:nth-child(1)").click(function () { // first menu item
$(body).css('background', 'http://example.com/image.png');
});
$(".menu-item a:nth-child(2)").click(function () { // second menu item
$(body).css('background', 'http://example.com/image.png');
});
$(".menu-item a:nth-child(3)").click(function () { // third menu item
$(body).css('background', 'http://example.com/image.png');
});
I don't know what you are trying but I could give you hints.
$(".menu-item a") // is an array/jquery collection of all elements that match the selector
.click(function () { // binds the function now to the click event of every item found
$(this); // is now the currently clicked element
// you can now traversal with $(this) for example
$(this).siblings(); // will be a collection of all surrounding elements
$(this).next(); // is the next element after the current one, can be chained like:
$(this).next().next(); // but I wouldn't recomand
$(this).prev(); // same as next but prev
$(this).parent(); // selects the direct parent element, in your case the li element
$(this).children(); // would select the direct children of the current element
// and so on.... there are much more possibilities
// on every of this possibilities you can do your background change
$("some selector"); // is of course still possible
// i think you are trying to do this:
var menuItems = $(".menu-item a");
menuItems.eq(0).css("background", "url to bg 1");
menuItems.eq(1).css("background", "url to bg 2");
menuItems.eq(2).css("background", "url to bg 3");
})
Look at the Traversing section of the jQuery docu. I would also always recommend to look what jQuery is actually doing. Many people hide behind jQuerys api and have no idea whats happening. This results into many misunderstandings.
You may try something like this
HTML:
<body>
<ul class="menu">
<li class="menu-item"><a name="blue" href="#">item 1</a></li>
<li class="menu-item"><a name="red" href="#">item 2</a></li>
<li class="menu-item"><a name="orange" href="#">item 3</a></li>
</ul>
</body>
CSS:
.red {
background:red;
}
.blue {
background:blue;
}
.orange {
background:orange;
}
Jquery:
$('.menu').on('click', 'a', function () {
var bgColor = $(this).attr('name');
$('body').removeClass().addClass(bgColor);
return false;
});
DEMO
The way I suggest going about this is to grab the position of the element that has been clicked. You can do this by using jQuery's index() function as other posters have suggested. Remember this will give you a zero-based index of the position which means that the position counting starts at 0 as opposed to 1.
Based on the item that has been clicked, you assign a CSS class to the target item which is the body element based on the sample code you provided.
Also, I noticed that your JS comments are still invalid even they were edited. Single line comments in JS use a double forward slash, // whereas multiline comments begin with /* and are closed by */.
This is my solution: http://jsfiddle.net/tgZUK/.
I have a ul with around five <li> items. E.g.
<ul>
<li>Step 1 : Take food</li>
<li>Step 2 : Go Around</li>
<li>Step 3 : Deliver</li>
</ul>
Also I have links like
Previous
and
Next
I have to show the first li at first. Then when the next link is clicked, it should now show 2nd <li> and so on. Same for previous link. Please help.
following is the complete code:
$(document).ready(function()
{
var ul = $('ul');
// hide all li
ul.find('li').hide();
// make first li as current
ul.find('li').first().addClass('current').show();
// setup previous click handler
$('a#prev').click(function()
{
var prev = ul.find('li.current').prev();
if( prev.length )
{
ul.find('li.current').removeClass('current').hide();
prev.addClass('current').show();
}
});
// setup next click handler
$('a#next').click(function()
{
var next = ul.find('li.current').next();
if( next.length )
{
ul.find('li.current').removeClass('current').hide();
next.addClass('current').show();
}
});
});
have a look at the aptly named jQuery Cycle plugin.
http://www.malsup.com/jquery/cycle/scrollhv.html
If you are only showing one element, all you need to do is use the DOM tree as a search. If you want the next element, find the element that is currently being shown, hide it, and show its next sibling. If you are doing previous, then hide the current item and select the previous sibling.
If you are unsure of how to do this, just Google around for DOM navigation. It isn't too bad.
If at all possible, I would simply use some naming convention for your LI (in the id attribute) that you could very quickly select using jQuery. For instance, if your shown element is going to have a class that the rest won't have, you can select that element quickly using jQuery, grab its id, and modify it in some way to select the previous or next element.
as boerema said something along these lines (its untested!)
put a class "selected" on a li that starts as being shown
<ul>
<li>Step 1 : Take food</li>
<li class="selected">Step 2 : Go Around</li>
<li>Step 3 : Deliver</li>
</ul>
$("#prev").click(function(){
$(".selected").hide().removeClass("selected").prev().show().addClass("Selected");
});
$("#next").click(function(){
$(".selected").hide().removeClass("selected").next().show().addClass("Selected");
});
here is a quick demo : http://jsbin.com/oduli4
var width = 500;
var height = 250;
var slide = 0;
var speed = 500;
var size = 0;
$(document).ready(function() {
size = $('#slider').find('li').length;
$('#slider').find('ul').width(width * size).height(height);
$('#slider li, #slider img').width(width).height(height);
$('#next').bind('click',function() {
if(slide > img_width * (size - 1) *(-1)) {
slide -= width;
slider(slide);
}
});
$('#prev').bind('click',function() {
if(slide < 0) {
slide += width;
slider(slide);
}
});
});
function slider(slideMargin) {
$('#slider ul').stop().animate({'left':slideMargin}, speed );
}