Vue.js multiple v-on:click events - javascript

Is there any way to have multiple v-on:click events on the same item?
Im trying to both show/hide toggle a navigation and do a css animation on the item that toggles the navigation.
<template>
<div>
<nav v-if="seen">
<ul>
<li><a href="#front" v-smooth-scroll>forside</a></li>
<li><a href="#services" v-smooth-scroll>ydelser</a></li>
<li><a href="#cases" v-smooth-scroll>cases</a></li>
<li><a href="#contact" v-smooth-scroll>kontakt</a></li>
</ul>
</nav>
<div #click="seen = !seen" #click="setActive" id="burger-container">
<div id="burger">
<span> </span>
<span> </span>
<span> </span>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
seen: false
}
},
methods: {
setActive (event) {
event.target.classList.toggle('open')
}
}
}
</script>

Why don't you just add the second event to the function, like this:
methods: {
setActive (event) {
event.target.classList.toggle('open')
this.seen = !this.seen
}
}

Related

Get child element from element through an event with Vue

Goal
What I'm trying to achieve is that whenever the user clicks on the .menu-item element, the dropdown element .menu-item-dropdown will get a class that will show the element.
Problem
Currently, when I click on the .menu-item class element, the returned value from the console.log in showMobileNavDropdown() returns null. When clicking, the event.target element is .menu-item-main and not .menu-item. This happens only when I click on the text of the li. Otherwise this works as intended.
What would be the best way to include the li text so that I can still grab the .menu-item-dropdown class?
Vue
<template>
<div>
<nav class="nav">
<div class="nav-left">
<img class="logo" src="/images/logo.svg" alt="" />
</div>
<div class="nav-center">
<ul class="menu-items">
<li
class="menu-item"
#click="showMobileNavDropdown($event)"
>
<!-- Main Title On Nav -->
<a class="menu-item-main" href="#">Company</a>
<!-- Dropdown -->
<div class="menu-item-dropdown">
<ul>
<li class="">
About
</li>
<li class="">
Contact
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="nav-right">
Contact us
</div>
<div class="hamburger" #click="openMobileNav">
<span class="ham-line"></span>
<span class="ham-line ham-line-2"></span>
</div>
</nav>
<div class="mobile-nav"></div>
</div>
</template>
<script>
export default {
methods: {
openMobileNav() {
var mobileNav = document.querySelector(".mobile-nav");
mobileNav.classList.toggle("showMobileNav");
},
// This function
showMobileNavDropdown(event) {
console.log(event); // This element returns 'menu-item-main' and not 'menu-item'
console.log(event.target.querySelector(".menu-item-dropdown"));
},
},
};
</script>
Assuming you have a longer list of possible click-targets and can't or won't just use refs, I would use event-delegation and in the click-handler check, whether the clicked element is inside a clickable parent and based on that, toggle a class on the parent, which can then be used to select the child in CSS, for example to change it's opacity.
Vue.component('foo-bar', {
methods: {
toggleSubItemVisibility({ target }) {
const item = target.closest('.nav-item');
// we check because the user might have clicked on the surrounding ul
if (item) {
item.classList.toggle('active');
}
}
},
template: `
<nav>
<ul #click="toggleSubItemVisibility">
<li class="nav-item">
<span class="nav-label">Foo</span>
<div class="nav-sub-item">More details...</div>
</li>
<li class="nav-item">
<span class="nav-label">Bar</span>
<div class="nav-sub-item">More details...</div>
</li>
</ul>
</nav>`
})
new Vue({
el: '#app'
});
nav ul {
list-style-type: none;
display: flex;
flex-direction: row;
gap: 12px;
}
.nav-label {
cursor: pointer;
}
.nav-sub-item {
opacity: 0;
transition: opacity 1s ease;
}
.nav-item.active .nav-sub-item {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<foo-bar></foo-bar>
</div>
The solution was to either grab the element, and if that returned null, grab the sibling. This way, it's always possible to select the .menu-item-dropdown element. When clicking on the element, it can either be a child or sibling.
Part of Solution
var element =
event.target.querySelector(".menu-item-dropdown") ||
event.target.nextElementSibling;
element.classList.toggle("hide_drop");

Materialize tabs persist active tab on page refresh

I am building a single page application using vue.js with materialize and I have a navbar with tabs to navigate to different pages on my website. Materialize's tabs has an active property that displays which tab is currently selected. Every thing this far works perfectly.
This is materialize's tabs reference
The Issue
If you refresh the web page the tabs active property 'resets' to its default position. So I am trying to figure out how to persist the state of the navbar's active property and then reassign it to router-link.
My Code
<ul id="tabs" class="tabs tabs-transparent" :class="{ 'navbar--color': !changeNavColor }">
<li class="tab" id="home">
<router-link
id="home"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="/"
>Home</router-link>
</li>
<li class="tab" id="services">
<router-link
id="services"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Services"
>Service</router-link>
</li>
<li class="tab" id="Preapproved">
<router-link
id="Preapproved"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="PreApproved"
>Get Pre-Approved</router-link>
</li>
<li class="tab">
<router-link
id="cars"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Cars"
>inventory</router-link>
</li>
<li class="tab">
<router-link
id="testimonials"
class="link"
:class="{ 'navbar--color': !changeNavColor }"
to="Testimonials"
>Testimonals</router-link>
</li>
</ul>
if (localStorage.currentTab) {
this.currentTab = localStorage.currentTab;
var activeTab = document.getElementById(this.currentTab);
activeTab.classList.add("active");
console.log("saved");
}
$(document).ready(function() {
$("a").click(function(event) {
this.currentTab = event.target.id;
console.log(this.currentTab);
});
});
$(document).ready(function() {
$(".tabs").tabs();
});
window.addEventListener("scroll", this.onScroll);
},
beforeDestroy() {
window.removeEventListener("scroll", this.onScroll);
},
watch: {
currentTab(newTab) {
console.log("watch");
localStorage.currentTab = this.currentTab;
}
}
any help is appreciated
So I think you can use hash mechanism by using window.location.hash. You can put the data-toggle attribute in every tag anchor. And just check for location.hash !== ''
But the more Vue way would be through client side storage. Check the link its from vue docs. Although it is for the input field but you will get the idea of how to persist info by keeping a reference in client storage and on reload getting your desired state from there onward. Hope it helps.
Remove the indicator that is nested inside the first li, and the navigation works fine.
<ul id="tabs" class="tabs tabs-transparent">
<li id="tab" class="tab">
Home
<li class="indicator"></li> <!-- THIS RANDOM NESTED INDICATOR IS THE ISSUE -->
</li>
<li id="tab" class="tab">
Service
</li>
<li id="tab" class="tab">
Get Pre-Approved
</li>
<li id="tab" class="tab">
inventory
</li>
<li class="indicator" style="right: 488px; left: 0px;"></li> <!-- CORRECT INDICATOR, AUTOMATICALLY GENERATED BY MATERIALIZE -->
</ul>
This is how I ended up implementing this. First all of your router-links or <a> have to have unique id's
mounted() {
if (localStorage.currentTab) {
this.currentTab = localStorage.currentTab;
var element = document.querySelector(this.currentTab);
element.classList.add("active");
}
$(document).ready(function() {
$(".tabs").tabs();
$("a").click(function(event) {
this.currentTab = "#" + event.target.id;
localStorage.currentTab = this.currentTab;
});
});
},
watch: {
currentTab(newTab) {
localStorage.currentTab = this.currentTab;
}
}

The onClick on the unodered list is not working?

The on onclick function which should call the function which is HTML inside Java-script (JSX). Does not seems to work??
Does onClick only works on a button or does it also work on the lists too?
class Top extends React.Component{
constructor(){
super();
this.searchjsx = this.searchjsx.bind(this);
}
searchjsx = () =>{
return(
<div id='searchdiv'>
<form id='searchform'>
<input type="text" id="input" name="search"></input>
</form>
</div>
);
}
render(){
return(
<div>
<div id="navbar">
<ul id="nav">
<li><a className="a" href='https://www.google.com/'>Home</a></li>
<li><a className="b" href='https://www.google.com/'>Profile</a></li>
<li><a className="c" href='https://www.google.com/'>Pricing</a></li>
<li onClick={this.searchjsx} id='sch'>Search..</li>
</ul>
</div>
<div>
</div>
</div>
);
}
}
The below worked for me:
import React from "react";
export default class Top extends React.Component {
state = {
showForm: false
};
searchjsx = () => {
console.log("Toggled showForm");
this.setState({ showForm: !this.state.showForm });
};
render() {
return (
<div>
<div id="navbar">
<ul id="nav">
<li>
<a className="a" href="https://www.google.com/">
Home
</a>
</li>
<li>
<a className="b" href="https://www.google.com/">
Profile
</a>
</li>
<li>
<a className="c" href="https://www.google.com/">
Pricing
</a>
</li>
<li onClick={this.searchjsx} id="sch">
Search..
</li>
{this.state.showForm ? (
<div id="searchdiv">
<form id="searchform">
<input type="text" id="input" name="search"></input>
</form>
</div>
) : (
""
)}
</ul>
</div>
<div></div>
</div>
);
}
}
Notes:
onclick should work on any element
You were trying to insert the form inside the onClick attribute. Instead, you should change the state of the component when li is clicked and based on the state show or hide the form
You don't seem to need a constructor in this example
import React from "react";
export default class Top extends React.Component{
state = {
show:false
}
showInput() {
return(
<div id='searchdiv'>
<form id='searchform'>
<input type="text" id="input" name="search"></input>
</form>
</div>
);
}
handleShow() {
this.setState({
show: !this.state.show
})
}
render(){
return(
<div>
<div id="navbar">
<ul id="nav">
<li><a className="a" href='https://www.google.com/'>Home</a></li>
<li><a className="b" href='https://www.google.com/'>Profile</a></li>
<li><a className="c" href='https://www.google.com/'>Pricing</a></li>
<li onClick={e => this.handleShow(e)} id='sch'>Search..</li>
{
this.state.show ? this.showInput() : null
}
</ul>
</div>
</div>
);
}
}
You have to maintain a state variable that can control when to display a component. In this case, show.

How to select all child elements with a specific class using the "this" keyword?

I am creating a navigation bar that allows a user to view a dropdown after hovering or clicking a link. Each link has a class called "nav_item". There is more than 1 dropdown menu; each one has a class called "dropdown". After hovering over "nav_item" the child element with a class called "dropdown" should be set to "display: block;".
Here's my nav bar code:
<nav>
<div id="nav_title">
Learn Web Design
</div>
<div class="nav_item">
Intro
</div>
<div class="nav_item">
Learn
<ul class="dropdown">
<li>
HTML
</li>
<li>
CSS
</li>
</ul>
</div>
<div class="nav_item">
About
<ul class="dropdown">
<li>
FAQ
</li>
<li>
The Author
</li>
</ul>
</div>
<div class="nav_item">
Support
</div>
<div class="nav_item">
Contact
</div>
</nav>
Here's the jQuery code that is obviously syntactically incorrect:
$(".nav_item").click(function() {
$(this.".dropdown").css("display", "block");
});
There's another method that is clearly incorrect in CSS:
.nav_item:hover {
this.dropdown {
display: block;
}
}
Try this:
$(".nav_item").click(function() {
$(this).find(".dropdown").css("display", "block");
});
To do something similar using hover:
$(".nav_item").hover(
function(){ $(this).find('.dropdown').css('display', 'block'); },
function(){ $(this).find('.dropdown').css('display', 'none'); }
);

How to change a session variable when clicking on `<a>` in Javascript

I can't find a way to make it works without using PHP and I don't want to use PHP. The thing is that I have a list with some options :
And I want to set a session variable when I click on one link.
I have tried to do <a onClick="displayAllContainers();">All containers</a> then in my JS I have tried to do
`function displayAllContainers() {
console.log("Display all");
}`
But it didn't work.
[EDIT] This fiddle doesn't work but I can put all the code without making a question that make 500lines so
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session'
import { InfosContainers } from '/both/collections/infosContainers.js';
import { InfosMachines } from '/both/collections/infosMachines.js';
import './main.html';
import '../imports/ui/controlPanel.js';
import '../imports/ui/container.js';
import '../imports/ui/machine.js';
import '../imports/ui/chart.js';
import '../imports/ui/controlPanel.html';
import '../imports/ui/container.html';
import '../imports/ui/machine.html'
if (Meteor.isClient) {
//subscribe the publication of the server
Meteor.subscribe('infosContainers');
Meteor.subscribe('infosMachines');
}
function displayAllContainers() {
console.log("Display all");
}
Template.body.onRendered(function () {
});
Template.body.helpers({
//return all objects of the collection machines with the specified state
infosMachines() {
return InfosMachines.find({});
},
//return all objects of the collection containers with the specified state
infos(state) {
return InfosContainers.find({stateContainer: state});
},
//return all containers
infosCtn(){
return InfosContainers.find({});
},
//return true if we have >=1 container running. It's the emergency button
urgence() {
return InfosContainers.find({stateContainer: 'running'}).count() > 0;
}
});
<!-- form containers -->
<div class="col-md-6 col-sm-12 col-xs-12">
<div class="x_panel">
<div class="x_title">
<h2>Containers</h2>
<ul class="nav navbar-right panel_toolbox">
<li><a class="collapse-link"><i class="fa fa-chevron-up"></i></a>
</li>
<li class="dropdown">
<i class="fa fa-wrench"></i>
<ul class="dropdown-menu" role="menu">
<li><a onClick="displayAllContainers();return false;">All containers</a>
</li>
<li>Only running containers
</li>
<li>Only paused containers
</li>
<li>Only stopped containers
</li>
</ul>
</li>
<li><a class="close-link"><i class="fa fa-close"></i></a>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="x_content">
<br/>
{{#each infosCtn}}
{{> container}}
{{/each}}
</div>
</div>
</div>
<!-- /form containers -->
You need to return false so as to prevent the default click behavior.
Just change this way:
<a onClick="displayAllContainers();return false;">All containers</a>
Try this,
HTML
<a onClick="displayAllContainers()" href="javascript:void(0);">All containers</a>
JS
function displayAllContainers() {
console.log("Display all");
}
EDIT
HTML
<a onClick="Meteor.call('displayAllContainers');
" href="javascript:void(0);">All containers</a>
JS
Meteor.methods({
'displayAllContainers': function(){
console.log("Hello world");
}
});
This works, JSFiddle
<script language="javascript">
function displayAllContainers(){
console.log("Display all");
}
</script>
<a onclick="displayAllContainers()">
All Containers
</a>
Write your function like below and it should work,
displayAllContainers = function(){
console.log("Display all");
}
I don't know why it works, I am putting a question for it, Will put link here. SO Question
Ok so thank you to everybody for you help because you have all helped me to found the solution and there is it :
1) I need to use template events in my JS
Template.body.events({
'click #anchorid': function (e) {
console.log("saucisse")
}
});
2) And I had to use the ID in the html but I can remove the onClick
<a id="anchorid">All containers</a>

Categories