The proper way to use hover event handler in Vue.js - vue.js

I am self-learning Vue.js and need help.
I am practicing event handlers now. I want the red box to become 100px wider when I hover over it. It works fine when I replace hover with click, But does not work with hover.
Appreciate it if you can point out what is wrong whit my code.
<!DOCTYPE HTML>
<html>
<head>
<title>v-on Event Handlers</title>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.redbox {
width: 100px;
height: 100px;
background: red;
}
.redboxwide {
width: 200px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="app">
<div v-bind:class="{redbox:!redboxhover, redboxwide:redboxhover}" v-on:hover="redbox();">Hover over me</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
redboxhover: false
},
methods: {
redbox: function () {
this.redboxhover = !this.redboxhover;
}
}
});
</script>
</body>
</html>

You need to use v-on:mouseover instead of v-on:hover.
Also you might need v-on:mouseleave for cleanup.
Here you could find more info about existing Events and mouseover

Use #mouseover like this:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.redbox {
width: 100px;
height: 100px;
background: red;
}
.redboxwide {
width: 200px;
height: 100px;
background: red;
}
</style>
<div id="app">
<div v-bind:class="{redbox:!redboxhover, redboxwide:redboxhover}" #mouseover="redbox();">Hover over me</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
redboxhover: false
},
methods: {
redbox: function() {
this.redboxhover = !this.redboxhover;
}
}
});
</script>

With the help of #tauzN and #Alex Kosh, I changed my code to the following. I added both #mouseover and #mouseleave.
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.redbox {
width: 100px;
height: 100px;
background: red;
}
.redboxwide {
width: 200px;
height: 100px;
background: red;
}
</style>
<div id="app">
<div v-bind:class="{redbox:!redboxhover, redboxwide:redboxhover}" #mouseover="redbox();" #mouseleave="!redbox();">Hover over me</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
redboxhover: false
},
methods: {
redbox: function() {
this.redboxhover = !this.redboxhover;
}
}
});
</script>

Related

Trouble with Vue methods

doing some Vue.js challenges for school and having trouble with a function that should trigger on a hover.
I need the div with the class 'redBox' to grow 10 pixels taller each time it is hovered over.
Here's my code:
<html>
<head>
<title>v-on Event Handlers</title>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.box{width:200px; height:200px; background:green; border:2px solid black; text-align:center; line-height:200px; color:white;}
.hidden{display:none;}
.redBox{width: 100px; height: 100px; background-color: red; margin: 2em;}
</style>
</head>
<body>
<div id="app">
<div v-bind:class="{box:true, hidden:boxHidden}">{{message}}</div>
<button v-on:click="showhide();">{{buttonText}}</button>
<div class="redBox" v-on:hover="hoverGrow();"></div>
</div>
<script>
var app = new Vue({
el: '#app',
data:{
boxHeight:200,
boxHidden : false,
message : 'Make me disappear!',
buttonText : "Hide",
hovered: false,
},
methods:{
showhide : function(){
console.log(this.boxHidden);
if(this.boxHidden){
this.boxHidden=false;
this.buttonText="Hide";
}else{
this.boxHidden=true;
this.buttonText="Show";
}
},
hoverBox : function(){
this.boxHeight = boxHeight + 10;
}
}
});
Any tips as to why this doesn't work? Right now nothing happens when I hover over the square.
Try using v-on:mouseover instead of v-on:hover. Also your function appears to be named hoverBox not hoverGrow. So v-on:mouseover="hoverBox();" should work in your redBox div.

How to implement html drag and drop using vue 3 composition API

currently drag and drop feature is working with vue2, i want to achieve same feature using vue3 composition api.
vue2 code:
<div id="app">
<div id="box-droppable1" #drop="drop" #dragover="allowDrop">
<h3>Draggaable area 1:</h3>
<hr>
<div class="" draggable="true" #dragstart="onDragging" id="123">
<h2>Drag mee</h2>
<p>this is a text</p>
</div>
<img id="img-draggable" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" draggable="true" #dragstart="drag" width="336">
</div>
<div id="box-droppable2" #drop="drop" #dragover="allowDrop">
<h3>Droppable area 2:</h3>
<hr>
</div>
</div>
Here is vuejs code done using vuejs options API.
JS:
new Vue({
el: '#app',
data(){
return {
};
},
methods : {
onDragging(ev){
console.log(ev);
ev.dataTransfer.setData("text", ev.target.id);
//this.$store.commit('module/namespace', status);
},
allowDrop(ev) {
ev.preventDefault();
},
drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
},
drop(ev) {
ev.preventDefault();
let data = ev.dataTransfer.getData("text");
console.log(data);
ev.target.appendChild(document.getElementById(data));
}
},
})
css:
#app{
width: 100%;
display: flex;
#box-droppable1 {
width: 50%;
background-color: coral;
min-height: 300px;
height: 70px;
padding: 10px;
border: 1px solid #aaaaaa;
}
#box-droppable2 {
width: 50%;
min-height: 300px;
height: 70px;
padding: 10px;
border: 1px solid #aaaaaa;
}
}
---------------------#---------------------------------------------------------------------------------------#------------------
codepen
As the comments already mention, this is nothing that would be different in the composition API, which is just another way to define a component.
All the methods you have in the options API, you can just have them in the setup method and return them:
setup() {
const onDragging = (ev) => {
console.log(ev);
ev.dataTransfer.setData("text", ev.target.id);
};
const allowDrop = (ev) => {
ev.preventDefault();
};
const drag = (ev) => {
ev.dataTransfer.setData("text", ev.target.id);
};
const drop = (ev) => {
ev.preventDefault();
let data = ev.dataTransfer.getData("text");
console.log(data);
ev.target.appendChild(document.getElementById(data));
}
return {
onDragging,
allowDrop,
drag,
drop,
}
}
I would probably not directly append a child with vanila js but also do it the Vue way, but that's just a side note.

Vue <transition-group> is not triggering when using v-move class?

I've been trying to get <transition-group> to work but I just can't figure it out. Upon clicking the box, the element will change its position - which is suppose to trigger v-move class. This is the specific part of Vue documentation that I'm looking at.
Here is the link to my test app
<style>
.test-group-leave-to{
opacity: 0;
}
.test-group-move{
transition: transform 5s;
}
</style>
<body>
<div id='app'>
<test-comp></test-comp>
</div>
<script>
let arrary = ['1','2','3','4','5','6','7','8','9'];
new Vue({
el: "#app",
components: {
"test-comp":{
template: `
<div style='display:inline-block;'>
<transition-group name='test-group'>
<div
class='box'
v-for='(all,ind) in arr'
:key='all'
#click='del(ind, $event)'>{{ all }}</div>
</transition-group>
</div>
`,
data(){
return {
arr: arrary
}
},
methods: {
del(ind, e){
e.target.style.left = '100px';
this.arr.splice(ind,1);
}
}
}
}
});
</script>
A bit different approach in order to achieve smoother animations:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Balsamiq+Sans:wght#400;700&family=Indie+Flower&family=Nunito+Sans:wght#400;600;700;800;900&display=swap" rel="stylesheet">
<style>
body{
position: relative;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
font-family: 'Nunito', sans-serif;
}
.outer-box {
position: relative;
width: 100%;
padding: 5px 10px;
transition: all 1s;
}
.box{
border: 1px solid blue;
text-align: center;
width: 50px;
height: 50px;
user-select: none;
}
.animate_move-enter, .animate_move-leave-to {
opacity: 0;
transform: translateX(100px);
}
.animate_move-leave-active {
position: absolute;
}
</style>
</head>
<body>
<div id='app'>
<test-comp></test-comp>
</div>
<script>
let arrary = ['1','2','3','4','5','6','7','8','9'];
new Vue({
el: "#app",
components: {
"test-comp":{
template: `
<div>
<transition-group name='animate_move'>
<div v-for='(all,ind) in arr' :key='all' class="outer-box">
<div class='box' #click='del(ind, $event)'>{{ all }}</div>
</div>
</transition-group>
</div>
`,
data(){
return {
arr: arrary
}
},
methods: {
del(ind, e){
this.arr.splice(ind,1);
}
}
}
}
});
</script>
</body>
</html>
This way you can avoid jumping blocks after they are removed.
I suggest to use List Entering/Leaving Transitions instead of move transition and you should remove e.target.style.left = '100px'; :
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Balsamiq+Sans:wght#400;700&family=Indie+Flower&family=Nunito+Sans:wght#400;600;700;800;900&display=swap" rel="stylesheet">
<style>
body {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
font-family: 'Nunito', sans-serif;
}
.box {
position: relative;
border: 1px solid blue;
margin: 10px;
text-align: center;
width: 50px;
height: 50px;
user-select: none;
}
.test-group-enter-active,
.test-group-leave-active {
transition: all 1s;
}
.test-group-enter,
.test-group-leave-to {
opacity: 0;
transform: translateX(100px);
}
</style>
</head>
<body>
<div id='app'>
<test-comp></test-comp>
</div>
<script>
let arrary = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
new Vue({
el: "#app",
components: {
"test-comp": {
template: `
<div style='display:inline-block;'>
<transition-group name='test-group'>
<div
class='box'
v-for='(all,ind) in arr'
:key='all'
#click='del(ind, $event)'>{{ all }}</div>
</transition-group>
</div>
`,
data() {
return {
arr: arrary
}
},
methods: {
del(ind, e) {
//e.target.style.left = '100px';
this.arr.splice(ind, 1);
}
}
}
}
});
</script>
</body>
</html>

How init Swiper in VueJS?

first of all thank you for your time because I spent all the morning on this issue. How use the https://idangero.us/swiper module on vue.JS ? Indeed Swiper is on the page but the parameters seems to be not taken in count.
I tried also vue awesome swiper but I prefer use the official version without bug. I tried to init swiper also in a const just after the import.
<template>
<div class="swiper-container">
<div class="swiper-wrapper">
<v-touch
#tap="navigateTo(item)"
v-for="item in subList"
:key="item._id"
class="swiper-slide"
>
{{t(item.sentence)}}
</v-touch>
</div>
</div>
</template>
<script>
import Swiper from 'swiper'
import 'swiper/dist/css/swiper.css'
import 'swiper/dist/js/swiper.js'
export default {
name: 'category',
data () {
return {
subList: [{some data}],
}
},
mounted () {
this.initSwiper()
},
methods: {
initSwiper () {
const mySwiper = new Swiper('.swiper-container', {
slidesPerView: 3,
slidesPerColumn: 2,
spaceBetween: 50
})
mySwiper.init()
}
}
}
</script>
<style scoped>
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
border: solid 2px white;
width: 200px;
height: 200px;
}
</style>
For example with this code I need to have a space between each div or only 2 lines but i have no space and 3 lines... Thank you for your help.
You can now use this Vue Awesome Swiper it has everything you need
you can install it using the following command
npm install swiper vue-awesome-swiper --save
Disclaimer: I have no affiliation nor a contributor to this package, I'm just merely using it. so I recommend it
You can simply use ref to init the slider like so:
<template>
<div>
<div ref="mySlider" class="swiper-container">
…
</div>
<button #click="next">Next Slide</div>
</div>
</template>
import Swiper from 'swiper'
import 'swiper/swiper-bundle.css'
export default {
data () {
return {
slider: null,
mySliderOptions: {
loop: true
}
}
},
methods: {
next() {
this.slider.slideNext()
}
}
mounted () {
this.slider = new Swiper(this.$refs.mySlider, this.mySliderOptions)
}
}
Update
They just released an official vue.js swiper component (only vue.js 3)
This seem to work, not sure if it is a good way to do it
Parent
<Swiper
:options="carouselOptions"
/>
Child (Swiper.vue)
<div v-if="options" ref="swiper" class="carousel-hero carousel-hero--is-hidden swiper-container">...
<script>
import Swiper, { Navigation, Pagination } from 'swiper';
Swiper.use([Navigation, Pagination]);
import 'swiper/swiper-bundle.css';
export default {
name: 'Swiper',
props: {
options: {
type: Object,
default: () => {
return {
slidesPerView: 1
}
}
}
},
data() {
return {
swiper: null,
}
},
mounted() {
let vm = this;
if (this.options && vm.$refs.swiper !== 'undefined') {
vm.$refs.swiper.classList.remove('carousel-hero--is-hidden');
this.swiper = new Swiper(vm.$refs.swiper, {
slidesPerView: this.options.slidesPerView,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
on: {
init: function () {
console.log('swiper initialized');
},
resize: function () {
console.log('resize');
}
}
});
}
},
methods: {
}
};
</script>
I had the same problem a long time ago.
Finally, I added Swiper from cdn, it worked well for me.
I made a simple example for you (with Swiper) hope it will help you.
I took all the CSS props + swiper config from here so feel free to play with it.
Let me know if you have any questions :)
You can also check these docs, it has an explanation on how to config it with Vue & React, etc.
new Vue({
el: '#app',
data: {
swiper: null
},
mounted() {
this.swiper = new window.Swiper('.swiper-container', {
cssMode: true,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
pagination: {
el: '.swiper-pagination'
},
mousewheel: true,
keyboard: true,
})
}
})
html, body {
position: relative;
height: 100%;
}
body {
background: #eee;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 14px;
color:#000;
margin: 0;
padding: 0;
}
.swiper-container {
width: 100%;
height: 100%;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
height: 200px !important;
background: pink;
border: 1px solid #888;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.2.2/css/swiper.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.5.1/js/swiper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
<div class="swiper-slide">Slide 4</div>
</div>
<!-- Add Arrows -->
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
<!-- Add Pagination -->
<div class="swiper-pagination"></div>
</div>
</div>
mounted : function(){
var swiper = new Swiper('.swiper-container', {
slidesPerView: 7,
spaceBetween: 0,
slidesPerGroup: 7
});
},

Uncaught TypeError: Cannot read property 'on' of undefined in arcgis

i am trying to display navigation tool and switch base map.Individually both are working good when i combine it its showing Uncaught Type Error: Cannot read property 'on' of undefined.can any tell me what is the mistake
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
<title></title>
<link rel="stylesheet" href="https://js.arcgis.com/3.15/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.15/esri/css/esri.css">
<style>
html, body, #map { height: 100%; width: 100%; margin: 0; padding: 0; }
#switch{
position:absolute;
right:20px;
top:10px;
z-Index:999;
}
#basemapGallery{
width:380px;
height:280px;
}
#HomeButton {
position: absolute;
top: 95px;
left: 20px;
z-index: 50;
}
#navToolbar{
display: block;
position: absolute;
z-index: 2;
top: 10px;
left:2px
}
.zoominIcon {
display: block;
position: absolute;
width: 16px;
height: 16px;
}
.zoomoutIcon {
position: absolute;
width: 16px;
height: 16px;
}
.zoomfullextIcon {
position: absolute;
width: 16px;
height: 16px;
}
.zoomprevIcon {
position: absolute;
width: 16px;
height: 16px;
}
.zoomnextIcon {
position: absolute;
width: 16px;
height: 16px;
}
.panIcon {
position: absolute;
width: 16px;
height: 16px;
}
.deactivateIcon {
position: absolute;
width: 16px;
height: 16px;
}
</style>
<script src="https://js.arcgis.com/3.15/"></script>
<script>
var map;
require([
"esri/map",
"esri/dijit/BasemapGallery",
"esri/dijit/HomeButton",
"esri/toolbars/navigation",
"dojo/on",
"dojo/parser",
"dijit/registry",
"dijit/Toolbar",
"dijit/form/Button",
"dojo/domReady!"
], function(
Map,
BasemapGallery,
HomeButton,
Navigation,
on,
parser,
registry
) {
parser.parse();
var navToolbar;
map = new Map("map", {
basemap: "topo",
center: [-105.255, 40.022],
zoom: 13,
slider:false
});
//add the basemap gallery, in this case we'll display maps from ArcGIS.com including bing maps
var basemapGallery = new BasemapGallery({
showArcGISBasemaps: true,
map: map
}, "basemapGallery");
basemapGallery.on('load',function(){
basemapGallery.remove('basemap_1');
basemapGallery.remove('basemap_2');
basemapGallery.remove('basemap_3');
basemapGallery.remove('basemap_4');
basemapGallery.remove('basemap_5');
basemapGallery.remove('basemap_8');
});
basemapGallery.startup();
basemapGallery.on("error", function(msg) {
console.log("basemap gallery error: ", msg);
});
var home = new HomeButton({
map: map
}, "HomeButton");
home.startup();
navToolbar = new Navigation(map);
on(navToolbar, "onExtentHistoryChange", extentHistoryChangeHandler);
registry.byId("zoomin").on("click", function () {
navToolbar.activate(Navigation.ZOOM_IN);
});
registry.byId("zoomout").on("click", function () {
navToolbar.activate(Navigation.ZOOM_OUT);
});
registry.byId("zoomfullext").on("click", function () {
navToolbar.zoomToFullExtent();
});
registry.byId("zoomprev").on("click", function () {
navToolbar.zoomToPrevExtent();
});
registry.byId("zoomnext").on("click", function () {
navToolbar.zoomToNextExtent();
});
registry.byId("pan").on("click", function () {
navToolbar.activate(Navigation.PAN);
});
registry.byId("deactivate").on("click", function () {
navToolbar.deactivate();
});
function extentHistoryChangeHandler () {
registry.byId("zoomprev").disabled = navToolbar.isFirstExtent();
registry.byId("zoomnext").disabled = navToolbar.isLastExtent();
}
});
</script>
</head>
<body class="claro">
<div id="map">
<div id="navToolbar" data-dojo-type="dijit/Toolbar">
<div data-dojo-type="dijit/form/Button" id="zoomin" data-dojo-props="iconClass:'zoominIcon'">Zoom In</div>
<div data-dojo-type="dijit/form/Button" id="zoomout" data-dojo-props="iconClass:'zoomoutIcon'">Zoom Out</div>
<div data-dojo-type="dijit/form/Button" id="zoomfullext" data-dojo-props="iconClass:'zoomfullextIcon'">Full Extent</div>
<div data-dojo-type="dijit/form/Button" id="zoomprev" data-dojo-props="iconClass:'zoomprevIcon'">Prev Extent</div>
<div data-dojo-type="dijit/form/Button" id="zoomnext" data-dojo-props="iconClass:'zoomnextIcon'">Next Extent</div>
<div data-dojo-type="dijit/form/Button" id="pan" data-dojo-props="iconClass:'panIcon'">Pan</div>
<div data-dojo-type="dijit/form/Button" id="deactivate" data-dojo-props="iconClass:'deactivateIcon'">Deactivate</div>
</div>
<div id="HomeButton"></div>
<div id="switch" data-dojo-type="dijit/TitlePane" data-dojo-props="title:'Switch Basemap', closable:false, open:false">
<div id="basemapGallery"></div>
</div>
</div>
</body>
</html>
parser.parse returns a deferred in dojo 1.8+
what this means is that after
parser.parse()
your widgets are not necessarily instantiated and ready to be referenced as widgets via dijit/registry.
Also there is this is directly from the Dojo reference guide:
Note that waiting for dojo/domReady! to fire is often not sufficient when working with widgets. Many widgets shouldn’t be initialized or accessed until the following modules load and execute:
dojo/uacss
dijit/hccss
dojo/parser
Thus when working with widgets you should generally put your code inside of a dojo/ready() callback:
you do this by including "dojo/ready" in your require array and then wrapping any widget code in
ready(function(){
...your widget code....
})
in your case you could probably just wrap your entire javascript code in a ready function
require([
"esri/map",
"esri/dijit/BasemapGallery",
"esri/dijit/HomeButton",
"esri/toolbars/navigation",
"dojo/on",
"dojo/parser",
"dijit/registry",
"dojo/ready",
"dijit/Toolbar",
"dijit/form/Button",
"dojo/domReady!"
], function(
Map,
BasemapGallery,
HomeButton,
Navigation,
on,
parser,
registry,
ready
) {
ready(function() {
var navToolbar;
map = new Map("map", {
basemap: "topo",
center: [-105.255, 40.022],
zoom: 13,
slider: false
});
...etc
I also like to use parseOnLoad = true which I find to be less prone to errors (both human and otherwise)
Just put this script element above the arcgis js script tag like so
<script type="text/javascript">
var dojoConfig = {
parseOnLoad: true
};
</script>
<script src="https://js.arcgis.com/3.15/"></script>
and get rid of the call to parser.parse() at the top of your script.