Fire event on Scrolling Vue.js overflow:auto height:100vh - vue.js

Hi I have problem firing event when I scroll. I want event to fire when I scroll menu and not whole page. If you look at console, and disable overflow and height in CSS for #app, event will fire. Please check link.

Assign id of inner div element. Let's say longlist here.
<div id="app">
<div id="longlist" class="some-long-list">
<ul>
...
</ul>
</div>
</div>
Add scroll event to the longlist element. Here you should use mounted() instead of created()
new Vue({
el: "#app",
data: {
list: null
},
methods: {
handleScroll (event) {
// Any code to be executed when the window is scrolled
console.log(event)
console.log("metallica")
}
},
mounted () {
this.list = document.getElementById('longlist')
this.list.addEventListener('scroll', this.handleScroll);
},
destroyed () {
this.list.removeEventListener('scroll', this.handleScroll);
}
})
Also, change css to enable scrolling only inside inner div.
#app {
background: #fff;
border-radius: 4px;
/* padding: 20px; */
transition: all 0.2s;
height: 100vh;
/* overflow: auto; */
}
.some-long-list {
background:red;
width: 300px;
margin:0px auto;
overflow: auto;
height: 100%;
}
Hope this could help.

You currently have this in your pen:
...
created () {
window.addEventListener('scroll', this.handleScroll);
},
...
...so your event listener is attached to the whole window. Try selecting the relevant element first with something like document.getElementsByClassName('my-list')[0] or similar to attach the event listener to the element itself.
e.g.
...
created () {
const list = document.getElementsByClassName('my-list')[0]
list.addEventListener('scroll', this.handleScroll);
}
...

Related

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.

Flickity Slider Animation and Gsap

The slider I am building have the active slider bigger than the others. I managed to make it work without the animation with flkty.reposition(). However, I am trying now to add the animation where the next slide grows in and the active decrease out. For The animation I am using GSAP.
The issue I am facing is to overwrite the left property with gsap so that it continuous animate. As of now, the left property (controlled by Flickity) does not take into account the final size (controlled by GSAP) of the selected slide.
https://codepen.io/stefanomonteiro/pen/VwzwjLw?editors=0010
As the left property of each slide is controlled by Flickity, we could use margin-left with a minus value as an alternative property to pull the selected slide to the left. I know margin is not a good property to animate but it works in this case without digging too deep into the Flickity core.
Here is the GSAP code:
gsap.to(slides, {
duration: 1,
width: "220px",
height: "336px"
});
gsap.to(selectedSlide, {
duration: 1,
marginLeft: "-248px", // the empty space calculated by newWidth - oldWidth
width: "468px",
height: "630px",
onComplete: () => {
// once all animations have been settled, we reset the margin
gsap.set(selectedSlide, { marginLeft: "" });
// and tell Flickity to update
flkty.resize();
flkty.reposition();
}
});
And the snippets
const animate = () => {
const flkty = Flickity.data(".carousel");
const selectedSlide = flkty.selectedElement;
const slides = flkty.getCellElements();
// remove the selected slides
slides.splice(flkty.selectedIndex, 1);
gsap.to(slides, {
duration: 1,
width: "220px",
height: "336px"
});
gsap.to(selectedSlide, {
duration: 1,
marginLeft: "-248px", // the empty space calculated by newWidth - oldWidth
width: "468px",
height: "630px",
onComplete: () => {
// once all animations have been settled, we reset the margin
gsap.set(selectedSlide, {
marginLeft: ""
});
// and tell Flickity to update
flkty.resize();
flkty.reposition();
}
});
};
new Flickity(".carousel", {
cellAlign: "right",
wrapAround: true,
percentPosition: false,
on: {
ready: () => animate()
}
});
const nextButton = document.querySelector(".flickity-button.next");
nextButton.addEventListener("click", () => animate());
/* external css: flickity.css */
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
}
.carousel {
background: #EEE;
}
.carousel-cell {
width: 220px;
height: 336px;
margin-right: 20px;
background: #8C8;
border-radius: 5px;
counter-increment: carousel-cell;
}
.carousel-cell.is-selected {
width: 468px;
height: 630px;
z-index: 1;
}
/* cell number */
.carousel-cell:before {
display: block;
text-align: center;
content: counter(carousel-cell);
line-height: 200px;
font-size: 80px;
color: white;
}
<link href="https://npmcdn.com/flickity#2/dist/flickity.css" rel="stylesheet" />
<script src="https://unpkg.co/gsap#3/dist/gsap.min.js"></script>
<script src="https://npmcdn.com/flickity#2/dist/flickity.pkgd.js"></script>
<h1>Flickity - wrapAround</h1>
<!-- Flickity HTML init -->
<div class="carousel">
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
<div class="carousel-cell"></div>
</div>
And the Codepen
You can also notice that we have to wait until the animation is finished until we perform the next click, otherwise, it would mess up the whole process. This is predictable. Hence, I personally will try not to manipulate this Flickity slider for this kind of animation. Just want to give you a solution, anyway.

How to make transition work with "visibility" but not "display"?

The transition element of vue only works with display:none but not visibility:hidden, is there any way to make it work with visibility? I want to get the clientWidth of the element before it shows up, with display:none I can't get that value.
By the way I'm using vue3.
Here is the reproduction demo:
https://codesandbox.io/s/competent-hermann-b1s5q
I'm going to assume, for the sake of argument, that you genuinely do need to use visibility for hiding and that other potential solutions (such as opacity) won't work in your real use case, possibly because they don't prevent user interactions with the element.
However, the assertion in the question is slightly misleading. It isn't really a difference between display and visibility. The real difference here is that the display case is using v-show, which includes special handling for transitions.
The current source code for v-show can be seen here:
https://github.com/vuejs/vue-next/blob/d7beea015bdb208d89a2352a5d43cc1913f87337/packages/runtime-dom/src/directives/vShow.ts
A similar approach can be used to construct a directive that uses visibility. Below is an example. It is based on the code for v-show but I've cut it back to just the code required for this particular use case:
const visible = {
updated(el, { value, oldValue }, { transition }) {
if (!value === !oldValue) {
return
}
if (value) {
transition.beforeEnter(el)
el.style.visibility = ''
transition.enter(el)
} else {
transition.leave(el, () => {
el.style.visibility = 'hidden'
})
}
}
}
Vue.createApp({
data() {
return {
show: true
};
},
methods: {
toggle() {
this.show = !this.show;
}
},
directives: {
visible
}
}).mount('#app')
#app {
text-align: center;
}
.tooltip-enter-active {
transition: transform 0.4s ease-out, opacity 0.3s ease-out;
}
.tooltip-leave-active {
transition: transform 0.35s ease-in, opacity 0.28s ease-out;
}
.tooltip-enter-from {
transition: none;
}
.tooltip-enter-from,
.tooltip-leave-to {
transform: translateY(-30px) scale(0.96);
opacity: 0;
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.prod.js"></script>
<div id="app">
<transition name="tooltip">
<div v-visible="show">
Using visibility
</div>
</transition>
<button #click="toggle">toggle message</button>
</div>
I did also have to make a small CSS change to give the enter transition a kick:
.tooltip-enter-from {
transition: none;
}
You'd probably be better off without <transition> in this case:
const app = Vue.createApp({
data() {
return {
show: true,
};
},
methods: {
toggle() {
const tooltip = this.$refs.tooltip;
this.show = !this.show;
tooltip.classList.toggle("tooltip-show");
},
},
mounted() {
console.log('Tooltip-width: ', this.$refs.tooltip.clientWidth);
},
});
app.mount('#app')
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.tooltip {
opacity: 0;
transform: translateY(-30px) scale(0.96);
transition: transform 0.35s, opacity 0.25s;
}
.tooltip-show {
opacity: 1;
transform: translateY(0) scale(1);
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.js"></script>
<div id="app">
<div class="tooltip" ref="tooltip">This will work!</div>
<button #click="toggle">toggle tooltip</button>
</div>

Vue onclick display specific item

I have a question about Vue.
I want to add a class to a specific item:
<p v-on:click="display = !display">Rediger joke</p>
Display is False before and it change it to true.
And it works. But my problem is, that this onclick is inside an v-for loop, and i only want to put "display" on one "update-site" and not all of them. Can i do this or do I have to try a different setup?
Thanks a lot
I have this idea that might help you. The idea is you extend post object with for example visible property and when you click event triggered you change this property and add .display class. Please check this jsfiddle
template
<div id="app">
<article v-for="post in filteredPosts" :key="post.id">
{{post.name}}
<button #click="display(post)">show</button>
<div class="post-content" :class="{display: post.visible}">this is the part I want to display onclick</div>
<hr />
</article>
</div>
css
.post-content {
display: none;
}
.post-content.display {
display: block;
}
code
new Vue({
el: '#app',
data() {
return {
posts: []
};
},
created() {
//when you load posts. add visible property.
setTimeout(() => {
//posts from server
var postsFromServer = [{
id: 1,
name: 'Post One'
},
{
id: 2,
name: 'Post Two'
}
];
//add visible proprty.
this.posts = postsFromServer.map(post => {
return {
...post,
visible: false
};
});
}, 1000);
},
computed: {
filteredPosts() {
//do your filters
return this.posts;
}
},
methods: {
display(post) {
this.$set(post, 'visible', !post.visible);
}
}
});
I have an article, and i get the data from Firebase.
<article v-for="post in filteredPosts" :key="post.id">
{{post.name}}
<p v-on:click="display = !display"></p>
<div>this is the part I want to display onclick</div
</article>
updateInputs has display:none, but onclick I want it to be display as block:
.updateInputs.display {
display: block;
position: absolute;
top:0;
left:0;
bottom: 0;
background-color: white;
box-shadow: 4px 4px 10px black;
width: 100%;
height: auto;
overflow: hidden;
overflow-y: scroll;
padding-bottom: 10px;
}

Window.resize or document.resize which works & which doesn't? VueJS

I am using Vuetable and its awesome.
I am trying to create a top horizontal scroll, which I have done and its working fine. But I need to assign some events on the window.resize.
I created a component such as
<template>
<div class="top-scrollbar">
<div class="top-horizontal-scroll"></div>
</div>
</template>
<style scoped>
.top-scrollbar {
width: 100%;
height: 20px;
overflow-x: scroll;
overflow-y: hidden;
margin-left: 14px;
.top-horizontal-scroll {
height: 20px;
}
}
</style>
<script>
export default {
mounted() {
document.querySelector("div.top-scrollbar").addEventListener('scroll', this.handleScroll);
document.querySelector("div.vuetable-body-wrapper").addEventListener('scroll', this.tableScroll);
},
methods: {
handleScroll () {
document.querySelector("div.vuetable-body-wrapper").scrollLeft = document.querySelector("div.top-scrollbar").scrollLeft
},
tableScroll() {
document.querySelector("div.top-scrollbar").scrollLeft = document.querySelector("div.vuetable-body-wrapper").scrollLeft
}
}
}
</script>
I am calling it above the table such as <v-horizontal-scroll />
I created a mixin as
Vue.mixin({
methods: {
setScrollBar: () => {
let tableWidth = document.querySelector("table.vuetable").offsetWidth;
let tableWrapper = document.querySelector("div.vuetable-body-wrapper").offsetWidth;
document.querySelector("div.top-horizontal-scroll").style.width = tableWidth + "px";
document.querySelector("div.top-scrollbar").style.width = tableWrapper + "px"
}
}
})
And I am calling it when the user component on which Vuetable is being created
beforeUpdate() {
document.addEventListener("resize", this.setScrollBar());
},
mounted() {
this.$nextTick(function() {
window.addEventListener('resize', this.setScrollBar);
this.setScrollBar()
});
},
I want to understand how this resizing event working.
If I change even a single thing in the above code. I am starting to have issues.
Either it doesn't set the width of scroll main div correctly or even this.setScrollBar don't work on resizing.
I am not clear what is the logic behind this and how it is working?