Add class to element on hover in Vue, without using data - javascript

In a Vue-component, I have a menu like this:
<ul class="menu-outer-wrapper">
<li>Foo 1</li>
<li class="has-children">
Foo 2
<ul>
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
<li>Foo 5</li>
<li class="has-children">
Foo 6
<ul>
<li>Child 1</li>
<li>Child 2</li>
</ul>
</li>
<li>Foo 7</li>
<li>Foo 8</li>
</ul>
And I would like to add the class hovered to the li.has-children-elements upon hover (mouseenter) (to be able to make some nicer animations for the children of that dropdown. And remove that class on mouseleave.
I know that there are options to do this with pure CSS, - but controlling delays and soft fade-in's are a pain (and become very messy very fast, without adding some classes).
I imagined doing something like this:
...
mounted(){
let liWithChildren = document.querySelectorAll( '.menu-outer-wrapper > li.has-children' );
liWithChildren.forEach( (event, window) => {
// Somehow add the class to the hovered element here.
// But I don't know how. Or if it's a good/bad idea (performance-wise).
}
}
But is that the way to go? And can I do it without using data (since the menu is dynamically generated by a CMS-system.
Update 1
I'm trying to keep my markdown readable. So I would like to avoid something like this:
<ul class="menu-outer-wrapper">
<li :class="[ { 'hovered' : someVar } ]">
Foo 1
</li>
<li :class="[ { 'hovered' : someVar }, 'has-children' ]">
Foo 2
<ul>
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
<li :class="[ { 'hovered' : someVar } ]">
Foo 2
</li>
...
...
...
Both since it won't gel with the dynamically generated menu.
And also since it add a lot of noise to the markdown.
Update 2
I simplified the example, to make it easier to digest. But due to the comments I figured I would elaborate on the dynamic generated menu. I'm making it something like this:
<nav id="secondary-menu" v-if="secondaryMenu">
<ul>
<li
:class="[ { 'has-children': r.children } ]"
v-for="(r, r_key, r_index) in secondaryMenu">
<a :href="r.url" :title="r.title">
{{ r.title }}
</a>
<ul class="children" v-if="r.children">
<li v-for="(c1, c1_key, c1_index) in r.children">
<a :href="c1.url" :title="c1.title">
{{ c1.title }}
</a>
</li>
</ul>
</li>
</ul>
</nav>

You just need the #mouseenter and #mouseleave events. All you need to do is listen for the appropriate events on all list-items that could have children, then perform your class addition (or removal) if the target element has the class of "has-children". Here's how I would do it:
<template>
<nav id="secondary-menu" v-if="secondaryMenu">
<ul>
<li
:class="[{ 'has-children': r.children }]"
v-for="(r, r_key, r_index) in secondaryMenu"
:key="`${r_key}-${r_index}`"
#mouseenter="addClass"
#mouseleave="removeClass"
>
<a :href="r.url" :title="r.title">
{{ r.title }}
</a>
<ul class="children" v-if="r.children">
<li
v-for="(c1, c1_key, c1_index) in r.children"
:key="`${c1_key}-${c1_index}`"
>
<a :href="c1.url" :title="c1.title">
{{ c1.title }}
</a>
</li>
</ul>
</li>
</ul>
</nav>
</template>
<script>
export default {
name: "HoverNav",
props: {
secondaryMenu: {
type: Array,
required: true,
},
},
methods: {
addClass: function (e) {
if (e.target.classList.contains("has-children")) {
e.target.classList.add("hovered");
}
},
removeClass: function (e) {
if (e.target.classList.contains("has-children")) {
e.target.classList.remove("hovered");
}
},
},
};
</script>
Here's a very unaesthetic sandbox of this in action. Let me know if this works for you :)
https://codesandbox.io/s/headless-brook-ysq97?file=/src/components/HoverNav.vue:0-1169

Related

How to get the text of the top most parent of list item in jquery?

I have following design of a ul list.
<ul>
<li class="category">
Design
<ul>
<li>Graphic Design</li>
<li>Web Design </li>
<li>HTML</li>
<li>CSS</li>
<li>Print</li>
</ul>
</li>
<li class="category">
Development
<ul>
<li>PHP</li>
<li>Java</li>
</ul>
</li>
</ul>
Now what I want is if you click on the "CSS" item ,I want to get the text of its parent i.e "Design".
I have tried the following code but I got the whole list in it.
$('ul').on("click", "li", function (e) {
console.log("Child Clicked: "+$(this).text()+" Parent: "+$(this).parents(".category").text());
});
Try replacing this part
$(this).parents(".category").text()
with
$(this).parents(".category")[0].childNodes[0].textContent;
childNodes returns all the child elements including text nodes.
If you want to use jQuery only, then wrap this textnode inside a span
<ul>
<li class="category">
<span>Design</span>
<ul>
<li>Graphic Design</li>
<li>Web Design </li>
<li>HTML</li>
<li>CSS</li>
<li>Print</li>
</ul>
</li>
<li class="category">
<span>Development</span>
<ul>
<li>PHP</li>
<li>Java</li>
</ul>
</li>
</ul>
and fetch that first node's text using
$(this).parents(".category span").html()
Another approach could be to use contents and filter of jquery
$(this).parents(".category").contents().filter(function() {
return this.nodeType == 3;
}).nodeValue;
just change :
console.log("Child Clicked: "+$(this).text()+" Parent: "+$(this).parents(".category").text());
with :
console.log("Child Clicked: "+$(this).text()+" Parent: "+$(this).parent().text());

Using jQuery with collapse menu

Currently my side menu consists of parent and child pages. To keep the menu expanded on the child page I am having to use a switch statement and check if the url is what I am looking for and if it matches show the expanded child items.
$(function () {
var url = window.location.pathname;
switch (url) {
case 'Path1':
$('#ChildOne').show();
break;
case 'Path2':
$('#ChildTwo').show();
break;
case 'Path3':
$('#ChildThree').show();
break;
...
}
});
I've got over 20 pages where I need to display the child elements. My question is, is this the only way or does someone know a better way? Thanks in advance for your help.
Note
I only want to display the relevant child elements on active child page. Suppose, I click on ChildOne when I get redirected to that page I only want to see the child elements under that parent.
My mark-up is as follows
<ul>
<li class="parent"><a style="cursor: pointer;">Parent One</a>
<ul class="child" id="ChildOne">
<li>Child Of Parent One</li>
<li>Child Of Parent One</li>
</ul>
</li>
<li class="parent"><a style="cursor: pointer;">Parent Two</a>
<ul class="child" id="ChildTwo">
<li>Child Of Parent Two</li>
<li>Child Of Parent Two</li>
<li>Child Of Parent Two</li>
</ul>
</li>
<li class="parent"><a style="cursor: pointer;">Parent Three</a>
<ul class="child" id="ChildThree">
<li>Child Of Parent Three</li>
<li>Child Of Parent Three</li>
</ul>
</li>
...
</ul>
I have added some code in order to be able to reproduce the issue. I added some classes to the parent items of the menu so that I can check them later in order to hide or not their children. The class names are the page name (final word on the url) which is unique. I get it using the substring method.
In this example, your current page is the second option, and by using the each() function of jquery, you can go through the elements without having a ton of switch cases or if statements.
Fiddle
Code snippet:
function escocha() {
var url = '/about/profile'
var n = url.lastIndexOf("/");
var myClassName = url.substring(n + 1, url.length)
alert("The url: " + url);
alert("The class name: " + myClassName);
$(".child").each(function (index) {
alert(this.id);
if (this.className.indexOf(myClassName) > -1) { // if any class name for this element contains the string 'display'
alert("I am the current page so my menu items won't collapse!");
} else {
$('#' + this.id).hide();
}
});
}
$("#esmaga").click(function () {
escocha();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="parent"><a style="cursor: pointer;">Parent One</a>
<ul class="child display" id="ChildOne">
<li>Child Of Parent One
</li>
<li>Child Of Parent One
</li>
</ul>
</li>
<li class="parent"><a style="cursor: pointer;">Parent Two</a>
<ul class="child profile" id="ChildTwo">
<li>Child Of Parent Two
</li>
<li>Child Of Parent Two
</li>
<li>Child Of Parent Two
</li>
</ul>
</li>
<li class="parent"><a style="cursor: pointer;">Parent Three</a>
<ul class="child display" id="ChildThree">
<li>Child Of Parent Three
</li>
<li>Child Of Parent Three
</li>
</ul>
</li>...</ul>
<button id="esmaga">Esmaga</button>
you can use id of ULs elements same as window.location.pathname. In this case you can call:
try{
$("#"+window.location.pathname).show();
}catch(e){
// error handling
}
but your menu is strange ;)
OK, one more version with object
var oPath = new Object({
"Path1":"ChildOne",
"Path2":"ChildTwo",
"Path3":"ChildThree"
});
try{
$("#"+ oPath[window.location.pathname]).show();
}catch(e){}

How to get a previous DIV with jQuery

I want to animate an element (.item-i) after I click on another element(.item-1) which is following below. Now I don't know, how to get to this element.
I want only the exact above element animated, not both above, only the first element above.
I made an easy show up in http://jsfiddle.net/Eef4v/1/. The YellowBG is the "animation".
Here is my JS code:
$('.item-1').click(function() {
$(this).closest('.item-i').css( "background", "yellow" );
});
And my HTML Markup:
<ul id="one" class="level-1">
<li class="item-i">I</li>
<li class="item-ii">II
<ul class="level-2">
<li class="item-a">A</li>
<li class="item-b">B
<ul class="level-3">
<li class="item-1">1</li>
<li class="item-2">2</li>
<li class="item-3">3</li>
</ul>
</li>
<li class="item-c">C</li>
</ul>
</li>
<li class="item-iii">III</li>
</ul>
<ul id="Ul1" class="level-1">
<li class="item-i">I</li>
<li class="item-ii">II
<ul class="level-2">
<li class="item-a">A</li>
<li class="item-b">B
<ul class="level-3">
<li class="item-1">1</li>
<li class="item-2">2</li>
<li class="item-3">3</li>
</ul>
</li>
<li class="item-c">C</li>
</ul>
</li>
<li class="item-iii">III</li>
</ul>
Use:
$(this).closest('.item-ii').prev().css( "background", "yellow" );
Working Demo
For Toggling background color:
$(this).closest('.item-ii').prev().toggleClass("highlight");
CSS:
.highlight {background: yellow;}
Demo with ToggleClass
Two ways to get .item-i to highlight.
$(this).parents('.level-1').find('.item-i').css( "background", "yellow" );
but if you have multiple .item-i classes under your .level-1 then they would all turn yellow.
Working Demo
You could also write a find algorithm that goes through each parent of .item-1 and finds the closest item-i
$('.item-1').click(function () {
$(this).parents().each(function(index,elem){
var query = $(elem).find('.item-i');
if(query.length > 0)
{
query.first().css("background-color", "yellow");
return false;
}
});
});
Working Algorithm demo

how to show hide element previce element and show one nested ul

I create this nested list to show and hide items but I want ask how can I show one list and hide other for example if user click on second subject will hide all open items
HTML
<ul>
<li class="subject">List item 1 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
<li class="subject">List item 2 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
<li class="subject">List item 3 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
</ul>
javascript
$(function(){
// $("ul li").children().slideDown("slow");
$(".subject").click(function(){
$(this).find("#item").slideToggle("slow");
});
})
CSS
#item
{
display: none;
}
$(".subject").click(function(){
$(this).find("#item").slideToggle("slow");
$(this).siblings().children('ul').slideUp();
});
DEMO
or
$(".subject").click(function () {
$(this).find("#item").slideToggle("slow")
.end().siblings().children('ul').slideUp();
});
DEMO
I corrected your markup. Your IDs should always be unique; otherwise use classes.
$(function(){
$(".subject").click(function(){
$('ul.item').not( $(this).find('ul.item') ).slideUp("slow");
$(this).find('ul.item').slideDown('slow');
});
});
WORKING JSFIDDLE DEMO

Strange issues with expanding and collapsing list elements in a Wordpress generated form

I'm hoping someone might be able to show me
a) what is causing the current issues
b) an altogether more elegant solution
for the following problem:
I am extracting all the pages out of a Wordpress blog and the idea is to create a menu where sub navigation expands and contracts as the user interacts.
The nested menu markup is essentially as follows:
<nav id="projects">
<ul class="menu">
<li class="page_item page-item-249">
Selected projects
<ul class="children">
<li class="page_item page-item-298"><a href="..." >Level 2</a></li>
<li class="page_item page-item-263"><a href="..." >Level 2</a></li>
<li class="page_item page-item-212"><a href="..." >Level 2</a></li>
</ul>
</li>
<li class="page_item page-item-238">
Archive
<ul class="children">
<li class="page_item page-item-33">
Level 2
<ul class="children">
<li class="page_item page-item-46">Level 3</li>
<li class="page_item page-item-48">Level 3</li>
</ul>
</li>
<li class="page_item page-item-35">
Level 2
<ul class="children">
<li class="page_item page-item-52">Level 3</li>
<li class="page_item page-item-57">Level 3</li>
<li class="page_item page-item-59">Level 3</li>
<li class="page_item page-item-61">Level 3</li>
</ul>
</li>
</ul>
</li>
</ul>
The jQuery is as follows :
$(".page_item a").click( function(){
/* Set vars */
var $this = $(this);
var isActive = false;
var hasSublinks = false;
var nextLevel = $(this).next("ul");
if ( $this.next().html() !== null ) {hasSublinks = true;}
if ( $this.hasClass('active') ) {isActive = true;}
if ( !hasSublinks ) {
return true;
} else {
/* I have sub links... */
if (!isActive) {
/* I am not active */
$(".active").removeClass("active");
$(".children").slideUp();
$(this).next("ul").slideDown();
$(this).addClass('active');
} else {
$(nextLevel).slideUp();
$(this).removeClass('active');
}
return false;
}
});
When I click on 'Archive', it expands its child list as expected. However, when I click on a link here, I expect it to slide down and reveal the next level (Level 3) but it has the opposite effect of sliding up to go back to where I started from.
I've spent a lot of time in the console, on Wordpress Codex trying to see what might be the issue, particularly challenging for me is that this code was working before (or seemed to). I'm aware that the code isn't the cleanest so I'm really hoping can show me where I'm going wrong!
Thanks
You don't seem to have the class active anywhere, so when you are doing this :
if ( $this.hasClass('active') ) {isActive = true;}
It set is Active to false
So you go in this part of code :
if (!isActive) {
/* I am not active */
$(".active").removeClass("active");
$(".children").slideUp();
There is clearly a problem with your logic, try fixing it, i'll give you a hint if you find nothing

Categories

Resources