I'm working in Vue.js and would like to conditionally display a back to top button when the user scrolls past a certain point. What am I doing wrong (no JQuery)?
In my template:
<div class="scroll">
<span class="scroll_button">Top</span>
</div>
In my mounted() function
const toTop = document.getElementsByClassName('scroll').addEventListener('scroll', function() {
if (window.scrollY > 0) {
this.classList.add('shown')
}
});
toTop();
data () {
return {
scrolled: false
};
},
methods: {
handleScroll () {
this.scrolled = window.scrollY > 0;
}
},
created () {
window.addEventListener('scroll', this.handleScroll);
}
Related
<div class="app__land-bottom" v-if="isVisible">
<a href="#projects">
<img ref="arrowRef" id="arrow" src="./../assets/down.png" alt srcset />
</a>
</div>
In Vue3 setup isn't working, but on Vue2 is working the following solution for not displaying a button based on scrolling.
VUE3
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
let isVisible = ref(false);
onMounted(() => {
window.addEventListener("scroll", () => {
hideArrow();
});
});
onUnmounted(() => {
window.removeEventListener("scroll", () => {
hideArrow();
});
});
const hideArrow = () => {
const currentScroll = window.pageYOffset;
if (currentScroll > 100) {
isVisible = false;
}
else if (currentScroll < 100) {
isVisible = true;
}
}
</script>
VUE2
<script>
export default {
created() {
window.addEventListener('scroll', this.hideArrow)
},
data() {
return {
isVisible: false,
}
},
methods: {
hideArrow() {
const currentScroll = window.pageYOffset;
if (currentScroll > 100) {
this.isVisible = false;
} else if (currentScroll < 100) {
this.isVisible = true;
}
},
},
}
</script>
In vue2 the solution is working but on Vue3, not. Any suggestions?
I don't understand exactly where the problem is? It would be helpful an answer.
when mutating your ref: isVisible you need to type: isVisible.value instead of isVisible
your hideArrow function should be:
const hideArrow = () => {
const currentScroll = window.pageYOffset;
if (currentScroll > 100) {
isVisible.value = false;
}
else if (currentScroll < 100) {
isVisible.value = true;
}
}
for more details: check Ref docs in Vue
what i want is when i click on textarea i want row value to be 10,
If you click outside the I want line 6
how can i do this i am newbie in vue js 3.
I couldn't figure out how to do this.
I couldn't figure out how to do about directives.
<template>
<div>
<textarea :rows="row"></textarea>
</div>
</template>
<script>
export default {
data() {
return {
row: 6
}
}
}
</script>
Try like following snippet:
const clickOutside = {
beforeMount: (el, binding) => {
el.clickOutsideEvent = event => {
if (!(el == event.target || el.contains(event.target))) {
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted: el => {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
const app = Vue.createApp({
data() {
return {
row: 6,
};
},
methods: {
setRow() {
this.row = 10
},
removeRow() {
this.row = 6
}
}
})
app.directive("clickOut", clickOutside).mount('#demo')
<script src="https://unpkg.com/vue#3.2.29/dist/vue.global.prod.js"></script>
<div id="demo">
<textarea #click="setRow" :rows="row" v-click-out="removeRow"></textarea>
</div>
So i have a directive it will run a function after it reached a certain threshold from the top of the viewport but i cannot seem to remove the event listener :/.
Here is the directive:
const scrollHandler = (el, binding) => {
const yOffset = el.getBoundingClientRect().y;
const isScrolled = document.body.scrollTop > (yOffset - 20) || document.documentElement.scrollTop > (yOffset - 20);
binding.value(isScrolled, el);
};
const GlobalDirectives = {
install(Vue) {
Vue.directive('scroll-watch', {
inserted(el, binding) {
window.addEventListener('scroll', () => scrollHandler(el, binding));
},
unbind(el, binding) {
window.removeEventListener('scroll', () => scrollHandler(el, binding), true);
console.log('is unbind');
},
});
},
};
and in the component:
<template>
<section v-scroll-watch="doSomething">
// html
</section>
</template>
<script>
export default {
methods: {
doSomething(val, el) {
console.log(val, el);
},
},
};
</script>
even when i navigate to another route somehow i can see that unbind is working but it still calls the event listener. can anyone tell me what im doing wrong?
[For question purpose, I make dummy example to make it easier to understand]
Let's say I have this homepage component that will toggle color when reach 1/3 of page height.
// Homepage.js
const Home = (props) => {
const [toggle, setToggle] = useState(false)
useEffect(() => {
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [toggle])
const handleScroll = () => {
const scrollPosition = document.documentElement.scrollTop;
const oneThirdPageHeight = (1 / 3) * document.documentElement.offsetHeight;
if (scrollPosition > onethirdPageHeight) {
return setToggle(true)
}
return setToggle(false)
}
return <div style={{ height: '1500px', color: toggle ? 'blue' : 'red'}}>hello</div>
}
and for the test file
// Homepage.test.js
test('should toggle color when scrolled', () => {
const { getByText } = render(<Homepage />);
const DivEl = getByText('hello');
expect(DivEl).toHaveStyle('color: red');
fireEvent.scroll(window, { target: { scrollY: 800 } });
expect(DivEl).toHaveStyle('color: blue'); // --> failing
});
It seems like I can't make the DOM scrolled by doing fireEvent.scroll(window, { target: { scrollY: 800 } }). Where did I miss? Please help, thank you in advance.
I've started to use Vue CLI but ran into a problem of handling window scroll position.
Just copied this example from Vue docs but it doesn't work.
This is my Nav.vue component:
<template>
<nav v-scroll="handleScroll"></nav>
</template>
<script>
export default {
name: 'my-nav',
data() {
return {
scrolled: false
}
},
directives: {
scroll: {
inserted: function (el, binding) {
let f = function (evt) {
if (binding.value(evt, el)) {
window.removeEventListener('scroll', f)
}
}
}
}
},
methods: {
handleScroll: function (evt, el) {
if (window.scrollY > 50) {
el.setAttribute(
'style',
'opacity: .5; background-color: red;'
)
}
return window.scrollY > 100
}
}
}
</script>
<style lang="scss" scoped>
nav {
position: fixed;
width: 100%;
height: 68px;
background-color: white;
z-index: 100;
}
</style>
An error occurs in this case:
error in ./src/components/Nav.vue
Module Error (from ./node_modules/eslint-loader/index.js):
error: 'f' is assigned a value but never used (no-unused-vars)
Also I searched other approaches how to handle scroll event but none of the worked.
In this case handleScroll method is just ignored:
<template>
<nav v-bind:class="{ hidden: scrolled}"></nav>
</template>
<script>
export default {
name: 'my-nav',
data() {
return {
scrolled: false
}
},
methods: {
handleScroll: function () {
this.scrolled = window.scrollY > 150;
}
},
created: function () {
window.addEventListener('scroll', this.handleScroll);
},
destroyed: function () {
window.removeEventListener('scroll', this.handleScroll);
}
}
</script>
<style>
.hidden {
opacity: .3;
}
</style>
It seemed to me that such simple things are much easier to resolve with Vue but I was wrong.
How to make scroll event work properly?
Your second approach should work, with one little caveat: you are not setting scrolled in the component data properly: you should be using this.scrolled, i.e.:
handleScroll: function () {
this.scrolled = window.scrollY > 150;
}
See proof-of-concept example:
new Vue({
el: '#app',
data() {
return {
scrolled: false
}
},
methods: {
handleScroll: function() {
this.scrolled = window.scrollY > 150;
}
},
created: function() {
window.addEventListener('scroll', this.handleScroll);
},
destroyed: function() {
window.removeEventListener('scroll', this.handleScroll);
}
});
body {
min-height: 200vh;
}
nav {
position: fixed;
top: 20px;
left: 20px;
}
.hidden {
opacity: 0.3
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<nav v-bind:class="{ hidden: scrolled }">NAV</nav>
</div>