arrow keys navigation through li (no jquery) - javascript

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.

Related

Apply the class "active" to nav links when clicked?

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

Copy div class from the clicked item and insert into another div

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

How do I simulate hover with Javascript on keydown?

First of, I'd like to use only native JavaScript to complete this task.
Let's say I am to make a custom dropdown, and the HTML code looks kind of like this.
<div class="dropdown">
<span class="dropdown-label" style="display:block">Select a thing</span>
<ul class="dropdownItemContainer">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</div>
In the CSS file I have something close to this:
ul.dropdownItemContainer li:hover {
background-color: #FF0000;
}
Yeah, there's really no dropdownish behavior, but it's not the point of discussion actually. The problem is that I couldn't think of a decent way to enable keyboard control for this dropdown. The desired outcome is the following: I press the down key, and the first option is highlighted; I press it again, and the second option is highlighted and so on.
The only option that I see at this point (just started studying JS) is to fetch all of the ul's children, stick'em into an array and assign the tags a background color through JS methods in a proper way whenever the down key is pressed.
On the other hand, I still have the :hover behavior described in the CSS for mouse countrol. Is there a smart way of simulating hovers?
I would go with a simple assignment of a class on your li-elements and steer it with a keydown handler. The following code is not meant to be complete but give you something you can work with.
var active = document.querySelector(".hover") || document.querySelector(".dropdownItemContainer li");
document.addEventListener("keydown",handler);
document.addEventListener("mouseover",handler);
function handler(e){
console.log(e.which);
active.classList.remove("hover");
if (e.which == 40){
active = active.nextElementSibling || active;
}else if (e.which == 38){
active = active.previousElementSibling || active;
}else{
active = e.target;
}
active.classList.add("hover");
}
You can see a working example here
You may want to use a library instead of coding this from scratch.
http://vebersol.net/demos/jquery-custom-forms/
http://www.dreamcss.com/2009/05/15-jquery-plugins-to-enhance-your-html.html
I would suggest removing the hover attribute from css.
And add only a hovered class which is applied on keypresses and on mouseover
This could look like this in Code
var dropDown = document.getElementsByClassName("dropdownItemContainer")[0]
document.addEventListener("keydown",function (e) {
if(e.keyCode == 38 || e.keyCode == 40 ) {
var key = e.keyCode
var hovered = dropDown.getElementsByClassName("hovered")
if(hovered.length != 0 ) {
cur = hovered[0]
cur.className = ""
cur = cur[(key==38?"previous":"next")+"ElementSibling"] || dropDown.children[key==38?dropDown.children.length-1:0]
} else {
cur = dropDown.children[key==38?dropDown.children.length-1:0]
}
cur.className="hovered"
}
});
dropDown.addEventListener("mouseover",function (e) {
for( var i = 0,j; j = dropDown.getElementsByClassName("hovered")[i];i++)
j.className = "";
e.srcElement.className = "hovered";
});
Heres an example on JSFiddle
Reality you didn't need any js for dropdown but You can use JavaScript Event for simulating it. You can use event like hover, focus, onclick
In JS You Can use This For Set Event
document.getElementById('id').addEventListener('focus',function(e){
//place code that want ran at event happened
}
In JQuery you can use bind, click ,...
$('#id')bind('focus',function(e){
//place code that want ran at event happened
}
List of Event
http://www.quirksmode.org/dom/events/index.html

jquery - on click of menu item or another element will change body background image

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/.

Stop jquery effect?? Or stop the javascript

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.

Categories