Navigating to upper-level list element in unordered list - javascript

I'm trying to navigate through an unordered list and having trouble making it work fully.
There are 4 buttons - Up, Down, Left, and Right. Navigating with Up or Down will correctly take you to the previous or next <li> (vertically) on that level, if one exists. Navigating Right will take you to the first list item inside the (down a level) in that <ul>, if an <ul> exists.
The left button should basically reverse that order (go up a level), but it doesn't.
Here is my code:
CSS
<style>
.btn { margin: 10px; padding: 10px; background-color: #999; cursor: pointer; display: inline-block; }
.current > span { color: red; font-weight: bold; }
</style>
HTML
<div class="left btn">Left</div>
<div class="right btn">Right</div>
<div class="up btn">Up</div>
<div class="down btn">Down</div>
<ul id="demo01">
<li class="current"><span>one</span></li>
<li><span>two</span></li>
<li><span>three</span>
<ul>
<li><span>four</span></li>
<li><span>five</span></li>
<li><span>six</span>
<ul>
<li><span>seven</span></li>
<li><span>eight</span></li>
</ul>
</li>
</ul>
</li>
<li><span>nine</span></li>
</ul>
JS / JQuery
<script>
(function($) {
$(document).on('click','.right', function() {
var current = $('#demo01 li.current');
if(current.has("ul").length){
right = current.find('li').first();
current.removeClass('current');
right.addClass('current');
}
});
$(document).on('click','.left', function() {
var current = $('#demo01 li.current');
if( current.parent.has('ul').length) {
left = current.parent('ul li');
current.removeClass('current');
left.addClass('current');
}
});
$(document).on('click','.up', function() {
var current = $('#demo01 li.current');
if( current.prev('li').length) {
up = current.prev('li');
current.removeClass('current');
up.addClass('current');
}
});
$(document).on('click','.down', function() {
var current = $('#demo01 li.current');
if( current.next('li').length) {
down = current.next('li');
current.removeClass('current');
down.addClass('current');
}
});
}(jQuery));
</script>
Current level of functionality:
Using the Up and Right buttons, you can succesfully navigate to "eight". The expectation is that when you click the Left button, to go back to the top, you'd go to "six" (not "four"), then "three" (not "one"). Right now, I'm getting nothing.
I thought using parent() jQuery function would be the way to go, but I must be writing it incorrectly.
I suppose each time I click through, I could add/remove a class ("last-visit"), and when "returning" do a find on that. I'd like to first know what I'm doing wrong with the code I've written.
If someone could point me in the right direction, I'd appreciate it.

You need to change:
if( current.parent.has('ul').length) {
to:
if( current.parent().has('ul').length) {
parent is not a property of the current object. It is the function parent().
You also need to change this line:
left = current.parent('ul li');
to:
left = current.parents('li');
left = current.parent('ul').parent('li'); // params are optional
Your above is selecting a direct (one-level up) ancestor ul and its interior li (which is the current current element). Then, it makes it not current. Therefore, this does not work.
The modified line looks for any ancestors (can be more than one level up) goes up two levels to get to the correct <li> element.
See a working example at JSFiddle.net.
EDIT See the comment below. This is more correct now.

Try this. You need to navigate to the li which parent to ul for 4,5,6 spans
$(document).on('click','.left', function() {
var current = $('#demo01 li.current');
if(current.parent().parent().closest("li").length)
{
left = current.parent().parent().closest("li");
current.removeClass('current');
left.addClass('current');
}
});
http://jsfiddle.net/go0yjoae/1/

Related

Closing one element when opening another within the same loop using Javascript

first time on here so i'll try my best to explain what I'm asking.
So I have 3 list items with the same class name. I've put them in a looping function so that when you click on one it will display a sub set of list items for that specific list item. I also have them inside an if statement that adds a new class name to the specific list item that was clicked. It allows opening and closing of the sub list items when you click the corresponding parent element.
My question is; how can I use this same principle of checking for the additional class name, when the user clicks any of the list items. In other words, I am trying to code it in a way that will allow me to close any of the open sub list items when the user clicks a new list item.
This is what I came up with but it doesn't know what button[i] is when I include it within the "click" function. What I was trying to do with this code is to take whatever list item was clicked, and then check the previous and next iterations of the class name "button" to see if any of the contain also contain the class name "clicked.
HTML
<div class="main">
<ul>
<li>One
<ul>
<li>One-1</li>
<li>One-2</li>
</ul>
</li>
<li>Two
<ul>
<li>Two-1</li>
<li>Two-2</li>
</ul>
</li>
<li>Three
<ul>
<li>Three-1</li>
<li>Three-2</li>
</ul>
</li>
</ul>
</div>
CSS
.main ul ul {
display: none;
}
.main ul ul li {
display: block;
}
Javascript
var button = document.getElementsByClassName("button");
for (i = 0; i < button.length; i++) {
button[i].addEventListener("click", function() {
var prevItem = button[i - 1];
var nextItem = button[i + 1];
if (prevItem.className !== "button") {
prevItem.className = "button";
prevItem.nextElementSibling.style.display = "none";
}
if (nextItem.className !== "button") {
nextItem.className = "button";
nextItem.nextElementSibling.style.display = "none";
}
if (this.className === "button") {
this.className += " clicked";
this.nextElementSibling.style.display = "block";
}
});
}
I am wanting to make this code usable no matter how many list items you add. So checking exactly button[0] button[1] and button[2] wasn't really an option, but I can see how button[i + 1] might not check every list item after it but rather just the next one. I tried adding another loop but ran into similar issues. anyway that's why I'm here. Thanks for any help in advance.
Since I am not sure whether I understood your question correctly, I quickly rephrase it in my own words.
Question: "I have an arbitrary number of list elements, of which each contains a button and a nested list. The button is always visible, the nested list is hidden by default. When the user clicks on a button, the corresponding nested list should be shown. At the same time, all other shown nested lists should be hidden again. How can I achieve this?"
The original HTML looks fine:
<div class="main">
<ul>
<li>One
<ul>
<li>One-1</li>
<li>One-2</li>
</ul>
</li>
<li>Two
<ul>
<li>Two-1</li>
<li>Two-2</li>
</ul>
</li>
<li>Three
<ul>
<li>Three-1</li>
<li>Three-2</li>
</ul>
</li>
</ul>
</div>
The CSS I did not fully understand, but I suggest the following:
.main ul ul {
display: none;
}
.main li.is-active ul {
display: block;
}
.main ul ul li {
display: block;
}
By adding the "is-active" class to an LI element, it is shown. This way, the CSS controls the visibility.
For the JavaScript part, I suggest this:
const buttonElements = Array.from(document.querySelectorAll('.button'));
buttonElements.forEach(buttonElement => {
buttonElement.addEventListener('click', () => {
const activeElements = Array.from(document.querySelectorAll('.is-active'));
activeElements.forEach(activeElement => {
activeElement.classList.remove('is-active');
});
buttonElement.parentElement.classList.add('is-active');
});
});
This solution assumes you can use newer versions of JavaScript/ECMAScript. Overall, it makes use of const and arrow functions.
First, we get all elements with the class "button" by using document.querySelectorAll(). Since the result is a NodeList and no array, we convert it using Array.from(). Afterwards, we loop through the array by using Array.prototpye.forEach(). We add an event listener for the "click" event. When a button is clicked, we search for all elements with the "is-active" class and for each one remove it. Finally, we add the "is-active" class to the parent element of the clicked button using Node.prototype.parentElement().
Here is another solution that works in older browsers:
var buttonElements = document.getElementsByClassName('button');
for (var i = 0; i < buttonElements.length; i++) {
buttonElements[i].addEventListener('click', function(event) {
var activeListElements = document.getElementsByClassName('is-active');
for (var i = 0; i < activeListElements.length; i++) {
activeListElements[i].setAttribute('class', '');
}
event.target.parentNode.setAttribute('class', 'is-active');
});
}
This is pretty much the same as the other approach but works with older versions of JavaScript.
Generally, the idea is to focus on an arbitrary sum of elements instead of an array with a specific length. In natural language something like: "Give me all buttons. For every button, add an event listener. When a button is clicked, give me all active list elements and remove their active status. Then, mark the list item above the button as active".
Hope this helps

Trying to do a:active to stay on untill other link is clicked

What I'm trying to do is this: I have two links hot/update. When hot is clicked it should turn red and update to be black. When update is clicked it should turn red and hot to be black.
This works on a Fiddle, but not on my website.
I was able to find various answers on SO, as this seems like a common thing to ask. I was implementing one by one, but none of them works. They seem to work fine in fiddle but not on my web.
HTML:
<div id="Space" >
<ul>
<li role="presentation" class="sort">
<a class="link" href="/?sort=score&page=1" style="text-decoration:none;">hot</a>
</li>
<li role="presentation" class="date">
<a class="link" href="/?sort=date&page=1" style="text-decoration:none;">update</a>
</li>
</ul>
</div>
JavaScript:
$(function() {
var links = $('a.link').click(function() {
links.removeClass('active');
$(this).addClass('active');
});
});
CSS:
a.link.active { color: red; }
a, a:visited { color: black }
Right now, this does what a:active does, it won't stay red.
var links = does not do what you think it does.
You are thinking it is doing this:
var links = $('a.link');
But, since you're assigning it to the actual click event, it's not resulting in that selector.
You need to revise your code as follows:
// This is a "safer" document ready, to prevent conflicts with other $ libraries
jQuery(function($) {
$('a.link').click(function() {
// Remove the class from all other links
$('a.link').removeClass('active');
// Add the class to _just this link_
$(this).addClass('active');
});
});
Or, a version that preserves the var links:
// This is a "safer" document ready, to prevent conflicts with other $ libraries
jQuery(function($) {
// Assign all links to the "links" variable
var links = $('a.link');
links.click(function() {
// Remove the class from all other links
links.removeClass('active');
// Add the class to _just this link_
$(this).addClass('active');
});
});
Here's a working fiddle: https://jsfiddle.net/cale_b/tqzt8f7s/1/
So to clarify you want certain buttons to be highlighted based on whether or not the url has a certain query. Highlight sort if url contains /?sort=score&page=1 and highlight date if url contains /?sort=date&page=1.
To do this all you need to do is search for each of those strings inside of your page's (window.location.search). The code for such a thing would be.
const highLightWhat = (window.location.search).search('sort=score') !== -1 ? 'Highlight sort' :
(window.location.search).search('sort=date') !== -1 ? 'Highlight date' :
'Highlight Nothing';
This flag then can be used as a reference to highlight one 'a' tag or another.
I hope this helped! (:
Try this javaScript once
$(function() {
$('a.link').click(function(event) {
event.preventDefault();
$('a.link').removeClass('active');
$(this).addClass('active');
});
});

mouseover event propagation issue - Manually propagate

I am implementing an User Interface for a project I'm working on and can be found here : Toobrok
Each time the mouse of the user enters a div, a class is added to this div to highlight it, I use the stopPropagation() method to restrict the highlighting to the div whose z-index is higher (the top div in the z axis).
However, sometimes, my user needs to select an element hidden by another one, when the dimensions of the 2 elements are different, and if the bottom div is larger, he can find some points of the bottom div not hidden by the top one, but when the dimensions are the same, I would like the user to be able to press a key to change the depth (on the z-axis) of his selection.
The relevant code is given below (in CoffeeScript), but a javascript solution would also help me:
Ui.bind = (elements, index) ->
ids = Ui.getIdSelector(elements)
$(ids).attr("centroid", index)
$(ids).mouseover (event) ->
event.stopPropagation()
Ui.highlight $(ids)
$(ids).mouseout (event) ->
event.stopPropagation()
Ui.resetHighlight $(ids)
I hope the question is clear and looking forward to your answer.
This is an example of HTML to consider :
<!DOCTYPE html>
<html>
<head>
<title> Sample page </title>
</head>
<body>
<div id="container">
<div id="child1">Some text...</div>
</div>
</body
</html>
And the related css :
#container {
height: 200px;
width: 500px;
}
#child1 {
height: 90%;
width: 90%;
}
When the mouse enters the child1 element, this element is highlighted, I want the container element to highlight when the user press a specific key.
I could use the JQuery parent() function to select that element on this example, but I am not sure it is a good solution, sometimes, the parent can have a size of 0px and and then a mouseover on this element would not be consistent. I want to select the element normally selected by Javascript if I do not use the stopPropagation() event.
I actually just found something that might help :
How to undo event.stopPropagation in jQuery?
But I cannot use that in my case... Because my condition is another user action, and I cannot synchronously wait for an user to do something.
I started writing code but then decided to leave implementation to you. Here is the text explanation:
At some point of time (probably when user press button to cycle through all hovered elements) you have to find all candidates for highlighting. There is no other way to do it rather than manually loop through all your elements and check if mouse position is inside their bound rect. You can get mouse coordinates from argument in mouseover callback. Save all these hovered elements in some array.
Next, you have to manually choose which element to highlight. Just highlight the first element in saved array and move the element to the end of array. You also may want to increase this element z-index and add callback for mouseout to this element.
Hope it helps, feel free to ask if you need more details.
You could use the CSS property pointer-events to make the child insensitive. Then events will be targeted to the element displayed below. For simple highlighting you should use pure CSS, however, jQuery can be helpful not to highlight the parent element as well while child is hovered without Ctrl.
Some example (also uploaded to JSFiddle, click into the output pane to make it responsive for keyboard events):
<div id="container1" class="container">
<div id="child1" class="child">Some text...</div>
</div>
div { border:1px dashed red; } /* for demo */
.container
{ height: 200px;
width: 500px;
}
.child
{ height: 90%;
width: 90%;
}
.insensitive
{ pointer-events:none;
}
.container:hover:not(.no-hilight),
.child:hover
{ background-color:yellow;
}
/* other color for demo */
.child:hover{ background-color:green; }
// make events passthrough child when <Ctrl> is held down
$(document).on('keydown keyup', function(ev) {
if (ev.key === 'Control') // for performance
$('.child')[ev.ctrlKey?'addClass':'removeClass']('insensitive');
});
// don't hilight container when child is hovered
$('.child').on('mouseover', function(ev)
{
$('.container').addClass('no-hilight');
});
// never suppress hilight when container is hovered directly
$('.container').on('mouseover', function(ev)
{ if(ev.target === ev.currentTarget)
$('.container').removeClass('no-hilight');
});
// just test which element a click is targeted to
$(document).on('click', function(ev)
{ console.log('click:', ev.target);
});
var preId = 0;
function makeBlack(id)
{
if(id)
{
$('#'+id).css('border-color','black');
}
}
function makered(id)
{
$('#'+id).css('border-color','red');
}
$(document).ready(function(){
$('div').mouseout(function() {
var currentid = this.id;
makeBlack(currentid);
preId = currentid;
});
$('div').mouseleave(function() {
var currentid = this.id;
makeBlack(currentid);
preId = currentid;
});
$('div').mouseover(function() {
var currentid = this.id;
makeBlack(currentid);
makered(preId);
preId = currentid;
});
$('div').mouseenter(function() {
var currentid = this.id;
makered(currentid);
preId = currentid;
});
});
Have you tried something like this for the CSS?
#container.hover{
height: 200px;
width: 500px;
//add a background-color to that element since its a div element
//background-color: (colour)
}
i should hope that the div element would automatically highlight the container div with whichever color you have selected

Koken CMS If class is add class to another element (multiple cases)

Besides a different color for each nav item when they are active i need to have my site-title (logo background color) another color for each page/album.
When a link inside the nav is active, .k-nav-current is automatically appended.
So i've tried this:
if ($("#main li:nth-child(2)").hasClass('.k-nav-current')) {
$("#site-title").addClass("babycurrent");
} else {
};
.babycurrent{
background: rgb(35,235,0);
}
Or instead of #main li:nth-child() i've tried:
#main a#unique_nav_itemid
a#unique_nav_itemid
But none of these solutions are working.
One method you can use to test to see if the element exists in the document:
if($('.k-nav-current').length) {
$("#site-title").addClass("babycurrent");
}
of course if length is 0, it is considered "falsey". any other positive number is "truthy".
However, for any further help, we will need to see the actual markup.
EDIT:
For your current setup, you are trying to use #main li:nth-child(2) and checking its class. The class is actually applied to the anchor. You need to use #main li:nth-child(2) a
if ($("#main li:nth-child(2) a").hasClass('k-nav-current')) {
$("#site-title").addClass("babycurrent");
}
Though it might be better to just use something like this (considering ids are unique):
if ($("#baby").hasClass('k-nav-current')) {
$("#site-title").addClass("babycurrent");
}
Lastly, if you really want it dynamic, you can do something like this:
var current = $("a.k-nav-current")[0].id.replace('#','');
$("#site-title").addClass(current + "current");
EDIT:
$( document ).ready(function() {
if ($("#baby").hasClass('k-nav-current')) {
$("#site-title").addClass("babycurrent");
}
});
// or you can do this (commented out) /*
$( document ).ready(function() {
var current = $("a.k-nav-current")[0].id.replace('#','');
$("#site-title").addClass(current + "current");
});
*/

jQuery Accordion misbehaving when expanding and contracting

I'm having trouble figuring out a bug in my accordion. The arrow icons are misbehaving when clicked on. If you go here, you will see some categories hidden by accordions. When you click on one and then close it, it behaves properly. But if you click on one, then click on another below before closing the first one, you'll notice that the arrow for the one above has returned to its "closed" position without closing it.
Here is the HTML that makes up each accordion:
<dl class="accordion">
<dt><strong>Accordion Title:</strong> Details</dt>
<dd>Hidden details</dd>
</dl>
The CSS for the arrows:
.accordion dt:after {
content: "\f125";
color: #ED4F30;
font-family: "Ionicons";
padding-left: 10px;
}
and
.accordion dt.accordion-active:after {
content: "\f123";
color: #ED4F30;
font-family: "Ionicons";
padding-left: 10px;
}
And finally the jQuery that I'm using to expand/collapse:
function ($) {
//Hide all panels
var allPanels = $('.accordion > dd').hide();
//Show first panel
//$('.accordion > dd:first-of-type').show();
//Add active class to first panel
//$('.accordion > dt:first-of-type').addClass('accordion-active');
//Handle click function
jQuery('.accordion > dt').on('click', function () {
var $this = $(this) ,
$target = $this.next();
jQuery('.accordion > dt.accordion-active').not($this.toggleClass('accordion-active')).removeClass('accordion-active');
$this.siblings('dd').not($target.addClass('active').slideToggle()).slideUp();
return false;
});
}
Any ideas what I'm missing?
It looks like you are overcomplicating things a little. You don't need to use the "not()" method to filter anything out. You are only toggling between 2 states (add/remove class, show/hide element) so you only need to use 2 jQuery methods, which were already in your code.
jQuery('.accordion > dt').on('click', function () {
var $this = $(this),
$target = $this.next();
$this.toggleClass('accordion-active');
$target.slideToggle();
return false;
});
Here's a JSFiddle based on the code you provided: http://jsfiddle.net/e5pe5/
Let me know if this is the intended functionality of your accordion.

Categories

Resources