I've been trying to achieve this for the last two days, but I'm fairly new to javascript, so maybe I'm just not seeing something.
What I'm trying to create is a Sidenavigation, that highlights the current section you are at. I have found a jquery plugin that does that like a charm http://trevordavis.net/blog/jquery-one-page-navigation-plugin/
But I am working with subitems and I would like to trigger the visibility of this subitem, as soon as the current section is active. So the ul would be visible if the containing list item has the class of .current, and if one of the sublist's list items has the class .current.
I have found out, that I'd probably need to trigger an event on the class change. I have tried the following, but it has not yet worked.
Markup:
<ul id="nav">
<li class="current">Section 1</li>
<li>Section 2</li>
<li class="parent">Section 3
<ul class="sublist">
<li>Subsection 1</li>
<li>Subsection 2</li>
<li>Subsection 3</li>
</ul>
</li>
</ul>
For the jquery I have tried this:
$('#nav').on('event', function(){
$('.parent').addClass('current').trigger('visibility');
});
$('.parent').on('visibility', function(){
$('.parent .sublist').addClass('visible');
});
What I am basically trying to do is what Bootrap does in its documentation. When scrolling down, you can see Glyphicons, as soon as you reached this section, the subitems pop open (available glyphs, how to use, examples) http://getbootstrap.com/components/
SASS applied to the Navigation so far:
.overview{
transition: .3s all;
ul{
margin-left: 10px;
ul{
max-height: 0;
transition: max-height 1s ease-out;
overflow: hidden;
}
}
}
.current{
> a{
font-weight: $bold;
}
ul{
max-height: 9999px !important;
transition: max-height 1s ease-out;
}
}
I have been able to show the sublist if the parent is set to current, but as soon as the child is current, the sublist will be hidden, so I figured, I'd need some javascript
I see now :)
What you could do, (there's probably multiple way, but what I'd do) is create a function that checks on scroll if a class is nearing the top of the window, if so, add a class to the relating item in the nav bar.
Should be relatively simple to do without a plugin, something along the lines of:
var checkSide = function(){
$('.container p').each(function(){
var item = $(this).offset().top,
$window = $(window),
navItem = $('#nav a[data-id="'+$(this).data('id')+'"]');
if ($window.scrollTop() >= item) {
if (!navItem.hasClass('current')) {
$('#nav .current').removeClass('current');
navItem.addClass('current');
}
}
});
};
$(window).scroll(function(){
checkSide();
});
See http://codepen.io/jhealey5/pen/GjJFI - Should be able to adapt it to your needs.
Assuming I understood correctly :)
Related
I have a nav menu that needs to trigger with clicks rather than hovers. When the links are clicked, an .open class would be added to the parent li. If that parent already has the .open class, then it would get removed. It would also be removed if another link is clicked on. So far I can get the class added when clicked and removed when a sibling is clicked, but not removed when it's already .open.
I tried adding a hasClass conditional, but that didn't work either. Seemed like it reruns the function every time it's clicked and therefore ignores the hasClass conditional.
Can anyone provide help? I tried toggleClass, but that didn't work.
$('li a').on('click', function() {
$('li a').parent().removeClass('open');
$(this).parent().addClass('open');
});
ul {
list-style: none;
}
li {
display: inline-block;
padding: 10px;
}
.open {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav>
<ul>
<li>
Item 1
</li>
<li>
Item 1
</li>
</ul>
</nav>
To do what you require you can use toggleClass() on the parent li when the element is clicked. To remove the class from all other li elements you can use removeClass() along with not() to exclude the current li. Try this:
$('li a').on('click', function() {
let $li = $(this).parent().toggleClass('open');
$('li').not($li).removeClass('open');
});
ul {
list-style: none;
}
li {
display: inline-block;
padding: 10px;
}
.open {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav>
<ul>
<li>
Item 1
</li>
<li>
Item 1
</li>
</ul>
</nav>
You can use
jquery toggleClass() to toggle yellow highlight (.open css class) on click/unclicking the same link.
jquery siblings() to remove .open class on all the other li items.
Below is the link for the demo
https://jsfiddle.net/so1u8hq6/
$('li a').on('click', function() {
$(this).parent().siblings().removeClass('open');
$(this).parent().toggleClass('open');
});
ul {
list-style: none;
}
li {
display: inline-block;
padding: 10px;
}
.open {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav>
<ul>
<li>
Item 1
</li>
<li>
Item 2
</li>
<li>
Item 3
</li>
</ul>
</nav>
Late to the party, but, after seeing the provided answers and some of the CSS you use I had to urge with my suggestions:
UX. Avoid styling LI tags in general, or at least set the desired display and move on. Style directly the a tag (with the necessary paddings etc.). You'll not only get less CSS to take care of, but also a larger touch interaction area. Makes no sense to style something yellow if it's not a UI part of something interactable. Also in JS, you don't need to take care about the LI wrappers any more - but only about the actual A Elements.
Don't use common selectors like $('li a') - those might target any LI→A elements in your app. Instead be more specific and use a Class like i.e: .tabs for the parent UL. Both in CSS and JS.
Try to use Event Delegation (in jQuey using the .on() method). Not only it will help you to catch the Event.delegateTarget parent UL where needed, but also the this (the clicked element), but mainly reference all the "group" of a elements enclosed in the common parent. That way you can have as many .tabs in a single page as you like. And yes, thanks to Event delegation you can even add dynamically LI Elements - and your JS will still work as expected.
Since you're using <a href="#"> Anchor elements, instead of (more properly) <button type="button>" Elements, you need to also use Event.preventDefault() in order to prevent the browser its default behavior and that's to follow anchors (scroll the page, navigate, etc...)
Use the selector "a.open" when you want to target and remove the "open" class. By just using "a" (or in other answers on this page - "li") you're uselessly touching elements trying to remove a class that's not there in the first place.
Finally, here's the CSS retouch and the proper jQuery needed for your task:
$(".tabs").on("click", "a", function(ev) {
ev.preventDefault();
$("a.open", ev.delegateTarget).not(this).removeClass("open");
$(this).toggleClass("open");
});
.tabs {
display: flex;
list-style: none;
padding: 0;
}
/* Style your Anchors, not the dummy LI wrappers */
.tabs a { padding: 10px; }
.tabs a.open { background-color: yellow; }
<ul class="tabs">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
To explain the only complicated line:
$(
"a.open", // Target just the ones (if any) of class "open"
ev.delegateTarget // inside the common ".tabs" ancestor
)
.not(this) // ... not the clicked element (since later we'll use .toggleClass on it)
.removeClass("open"); // ... remove that class "open"
the rest is pretty self explanatory.
Further read:
jQuery Event Delegation
jQuery event.delegateTarget
Event.preventDefault
So you only want the yellow background to appear as a signifier of user interaction rather than for the background color to be displayed? Have you tried using the mousedown/mouseup functions instead of .on('click', function(){...}?
I was able to simulate the click event where the color showcases via this method:
$('li a').mousedown(function() {
$('li a').parent().removeClass('open');
$(this).parent().addClass('open');
});
$('li a').mouseup(function() {
$('li a').parent().removeClass('open');
});
ul {
list-style: none;
}
li {
display: inline-block;
padding: 10px;
}
.open {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav>
<ul>
<li>
Item 1
</li>
<li>
Item 1
</li>
</ul>
</nav>
I have a ul containing floated li elements of variable width that wrap around inside of a container into multiple rows. I am removing li elements from the ul, and would like the height of the container to animate to accommodate the potential removal of a row.
If I am removing from the last row, I am handling this by animating the height of the li to zero before removing it.
The problem arises when I remove an element from a row other than the last row, and it vacates enough space to pull all of the elements in the last row into the previous row. Since I'm not removing an element in the last row, my li height animation doesn't do anything, causing a jump in the height of the ul and container when that last row is vacated.
Is there a way to handle this or am I stuck with this jump in this special case?
For example:
https://jsfiddle.net/d4v5kyty/1/
EDIT: My solution is posted below and here is a working fiddle: https://jsfiddle.net/x3qt0m89/1/
If you do not need to remove the elements from the DOM I would advise you to add a transition property to the LI elements and "remove" them by making them hidden on the canvas.
I've create this jsFiddle with an example that goes like this:
The normal HTML:
<!-- Just to remove the elements on the demo -->
<button class="js_removable">
Remove last item
</button>
<ul class="transitionable">
<li class="removable width-1">Content</li>
<li class="removable width-2">Content</li>
<li class="removable width-3">Content</li>
<li class="removable width-4">Content</li>
<li class="removable width-5">Content</li>
<li class="removable width-3">Content</li>
<li class="removable width-2">Content</li>
<li class="removable width-5">Content</li>
</ul>
The CSS would include the floated elements with variable width and the ul as their parent:
.transitionable {
width: 100%;
background-color: black;
overflow: hidden;
}
.removable {
float: left;
height: 50px;
border: 1px solid;
transition: all 0.5s ease-in-out;
}
.removable.hidden {
height: 0px;
/* you can further remove it from the dom with the visibility property, this is just for demo reasons */
}
.width-1 {
width: 25%;
background-color: white;
}
/* ... rest of the variables widths */
And the JS to remove the elements:
$('.js_removable').click(function () {
// Select only the not hidden elements .removable:not(.hidden)
$('.removable:not(.hidden)').last().addClass('hidden');
// You can further remove it from DOM if necessary after the animation ends
});
It's hard to really give a definitive answer since you didn't provide your code, but from what it sounds like, you're just missing some transitions. Try adding transition codes to the container and <ul>, and see what happens.
I ended up using jQuery to solve this issue by applying inline styles to the container when the page loads, and then after appending to removing from the ul calculating its height and animating the container to the height of the ul, like so:
JS:
var container = $('.container');
container.attr('style','height:' + $('.container').css('height') + ';');
var animateContainer = function () {
var ulHeight = $(container.find('ul')).css('height');
container.animate({'height': ulHeight}, 250);
}
$('.js_removable').click(function () {
$('.removable:not(.hidden)').first().addClass('hidden');
setTimeout(function(){
animateContainer();
}, 1000)
});
because there is a delay in this happening, there is a time when the ul can be larger than the container (if appending to the ul). I had no problem with this, and just added overflow: hidden; to the container to clean up the effect.
Not sure if this is the best method, but it works well for my purposes.
Here is a working fiddle: https://jsfiddle.net/x3qt0m89/1/
I have a list and would like to move the item to the top when it's clicked on. Simultaneously, all other items should move down to make space.
That's what I have so far:
<ul id="list">
<li id="one">item-1</li>
<li id="two">item-2</li>
<li id="three">item-3</li>
<li id="four">item-4</li>
</ul>
//
$('li').on('click', function () {
$(this).css({ position : 'absolute', top : $(this).position().top });
var height = $(this).parent().children().first().height();
var list = $(this).parent();
$(this).animate({top: '0px'}, { duration: 500, queue: false });
list.children().each(function( index, element) {
var $liElement = $(element);
if($liElement != $(this))
{
$liElement.animate({top: height + 'px'}, { duration: 500, queue: false });
}
});
});
Here is the link: http://jsfiddle.net/5qgnjvdp/
I see the item moving on top but all others don't move at all. What is wrong here?
Should I use prepend() to insert the list item on top when the animation is finished?
While not really an "animation", I was working on something some time ago along similar lines. What I ended up using was a slideUp, then a slideDown to give a sense of animation without actual animation connected to the move.
$('li').click(function() {
$(this).slideToggle(500, function() {
$(this).prependTo('#list').slideToggle(500); });
});
li { list-style: none; background: #ddd;padding: 5px 10px; border: 1px solid #aaa; margin: 10px; text-transform: uppercase; font-weight: bold;color:#fff; }
#list { margin:20px; padding:0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<ul id="list" data-role="listview">
<li id="one">item-1</li>
<li id="two">item-2</li>
<li id="three">item-3</li>
<li id="four">item-4</li>
</ul>
$('li').click(function() {
$(this).slideToggle(500, function() {
$(this).prependTo('#list').slideToggle(500); });
});
fiddle update
Again, I realize this isn't really an animation beyond the slide toggles, but it conveys a sense of interactivity similar to an animation.
There are a couple of problems with your script.
When comparing $liElement != $(this) you are in the scope of the each function where this refers to the current list child, which is the same as element. Store the clicked li in a variable outside of the looping function and compare with that. Also no need to compare the $(...) objects. element != clickedLi would suffice.
When you animate the other li elements they don't have position: absolute (or relative) so you won't see their movement.
If you assign the other li elements an absolute position you move all of them to the same position, the second row in the list. You could use position relative to move them down. You should just make sure to exclude the elements that come after the original position of the clicked li.
After the animation is complete you should insert the list item as the first child of the list and then remove the absolute/relative positioning, including the top style to make sure it works a second time (and to ensure that the visible representation is in line with the elements' structure.
So basically, I have an un-ordered list with around 12 list items. I would like to apply a style to them when the mouse is over any of them, and a different style when the mouse is over none of them.
For example, the following is my current code that only has a style when li's are being hovered.
HTML:
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
<li>List item 4</li>
<li>List item 5</li>
<li>List item 6</li>
</ul>
jQuery:
$('li').bind('mouseenter',function(){
$(this).animate({'opacity': '1'},100,'easeOutBack');
});
$('li').bind('mouseleave',function(){
$(this).animate({'opacity': '0.7'},600,'easeOutBack');
});
DEMO
I would like to style all the li to have opacity:1 when no li is being hovered over.
How can I achieve this?
You can do this with CSS only, if I understand correctly, you want to have all items at full opacity when none is hovered, and have full opacity on the one that's hovered and less opacity on the rest. Here's an example with just CSS:
li {
opacity: 1;
}
li:hover {
opacity: 1 !important;
transition: opacity .3s ease-in-out;
}
ul:hover li {
opacity: .5;
transition: opacity .3s ease-in-out;
}
Demo: http://jsfiddle.net/Sbgn8/2/
Is this what you want?
$('li').bind('mouseenter',function(){
$(this).animate({'opacity': '1'},100,'easeOutBack');
});
$('li').bind('mouseleave',function(){
$(this).animate({'opacity': '0.7'},600,'easeOutBack');
});
$("ul").bind('mouseenter',function(){
$("li").animate({'opacity': '0.7'},200,'easeOutBack');
})
$("ul").bind('mouseleave',function(){
$("li").animate({'opacity': '1'},200,'easeOutBack');
})
You can create a new class for the ul element that will cause all lis to be 100% opaque. Then, upon hovering a li, remove that class from the ul (its parent).
I'll give you a code if I sounded confusing.
Note that I've added a CSS transition, I suggest you do all of them using CSS (that way you won't have to deal with inline rules overriding all others).
$('li').bind('mouseenter',function(){
$(this).animate({'opacity': '1'},100);
$(this).parent().removeClass('full');
});
$('li').bind('mouseleave',function(){
$(this).animate({'opacity': '0.7'},600, function(){
$(this).css('opacity', '')
});
$(this).parent().addClass('full');
});
Here's what I had on my mind: http://jsfiddle.net/fqVT8/1/
Can't you just change the selector inside the handler function? Changing this...
$(this).animate({'opacity': '1'},100,'easeOutBack');
to this....
$('li').animate({'opacity': '1'},100,'easeOutBack');
I want to do this(photoshop image) on my page:
On hover(over the ACTORS link) I want the yellow drop down list(where Fotis J.Colakides etc is) to show up slowly. From up till down.
I don't know what is the best way to do this on my page.
Of course I have to use an unorder list() like this:
<ul id="showInfoNav">
<li><a class="slideBtn" href="#">MEDIA</a>
<ul class="slideShow">
<li>assa</li>
</ul>
</li>
<li><a class="slideBtn" href="#">ACTORS</a>
<ul class="slideShow">
<li>ACTOR1</li>
<li>ACTOR2</li>
</ul>
</li>
</ul>
[Update]
Util now I have done this: (http://jsfiddle.net/bMGhC/)
But I am doing it from css. I have the left:-9999px; and on hover I am doing it left:0px;
I want to do it slideDown. But it is not working.
here is my css:
ul#showInfoNav
{
list-style:none;
float:left;
width:100%;
}
ul#showInfoNav li
{
float:left;
margin-right:50px;
position:relative;
}
ul#showInfoNav a.slideBtn
{
padding:5px;
display:block;
text-decoration:none;
}
ul#showInfoNav ul a:hover{
color:#898989;
}
/*--- DROPDOWN ---*/
ul#showInfoNav ul
{
background-color:#F4F9B6;
padding:50px 10px 10px 10px;
list-style:none;
left:-9999px; /* Hide off-screen when not needed (this is more accessible than display:none;) */
position: absolute;
top:-20px;
/*z-index:-1;*/
}
ul#showInfoNav ul li
{
float:none;
}
ul#showInfoNav ul a{
white-space:nowrap;
}
ul#showInfoNav li:hover ul{ /* Display the dropdown on hover */
left:0; /* Bring back on-screen when needed */
}
I have also this z-index problem. If I set it -1 it is hidden behind the image too.
Check out jQuery UI effects, specifically slide():
http://docs.jquery.com/UI/Effects/Slide
You could also look into the slideToggle() method that jQuery supplies.
See an example here.
Similar functions would be slideUp(), slideDown() and the more general, animate()
Take a look at .hover(), and .slideUp/Down(). You can also replace .hover with .toggle in the following code, if you'd like a more 'permanent' state. The key to both is utilizing their callback functions.
HTML
Click to see a list
<ul>
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
<li>List Item</li>
</ul>
jQuery
$('ul').hide(); // doing this rather than via CSS for a fail-safe for JS
$('a').hover(function() {
$('ul').slideDown('fast');
}, function() {
$('ul').slideUp('slow')
})
When using jQuery fadeIn() you can specify the duration too.
Some simple sample would be:
Give the li's some id or class ( <li id="actor1">soemthing</li>)
In the JS file or script tag use:
$("#actor1).hover(function(){ $(this).slideDown('slow')}, function(){ $(this).slideUp('slow') });
EDIT: I see that you want a sliding effect so, I changed fadeIn() and fadeOut() to slideDown() and slideUp(). You can read their documentation here
This is the solution I found, I used this because I didn't want the <ul> to slideUp when I was navigating among his <li> tags.
$('ul.slideShow').hide();
$("#showInfoNav li").click(function () {
$(this).find('ul.slideShow').slideDown('fast').show();
$(this).hover(function () { // PARENT
}, function () {
$(this).parent().find("ul.slideShow").slideUp('slow'); //When the mouse hovers out of the subnav, move it back up
});
});