Good Day, I'm very new to Vue.js and want a navbar, which is transparent by default, but changes its background on scrolling. Unfortunately, it does not work. I tried few solutions, but none of this worked. So this JavaScript code is an example from Stack Overflow, which works in a Fiddle. If you need more information and/or code, please let me know.
Navigation.vue
<template>
<div id="navigation">
<nav class="nav-items">
<router-link class="item" to="/home">Home</router-link>
<router-link class="item" to="/about">About</router-link>
<router-link class="item" to="/japan">Japan</router-link>
</nav>
</div>
</template>
<script>
export default {
name: 'navigation'
}
import scroll from '../assets/js/scroll.js';
</script>
scroll.js
const navbar = document.querySelector('#navigation')
window.addEventListener('scroll', function(e) {
const lastPosition = window.scrollY
if (lastPosition > 50 ) {
navbar.classList.add('colored')
} else if (navbar.classList.contains('colored')) {
navbar.classList.remove('colored')
} else {
navbar.classList.remove('colored')
}
})
navigation.scss
FYI: I've removed unneccessary code here.
#navigation {
background: transparent;
.colored {
background: #fff;
transition: 0.3s;
}
}
Note: To view how to import custom code in a Vue component (general case), scroll down past the last <hr>.
Vue is a JavaScript framework and therefore you can insert vanilla code anywhere in it and it will run perfectly fine.
IMHO, you issue is not about importing vanilla code. It's about running it at the correct moment.
You have to run your code inside mounted() hook, because that's when #navigation exists in DOM:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('navigation', {
template: '#navigationTemplate',
mounted() {
window.addEventListener('scroll',
() => document.querySelector('#navigation')
.classList.toggle('colored', window.scrollY > 50)
)
}
})
new Vue({
el: '#app'
});
#app {
min-height: 200vh;
background: url("https://picsum.photos/id/237/1024/540") no-repeat center center /cover;
}
#navigation {
background: transparent;
position: fixed;
top: 0;
padding: 1rem;
transition: 0.3s;
width: 100%;
box-sizing: border-box;
color: white;
}
#navigation.colored {
background: rgba(255, 255, 255, .85);
color: black;
}
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script id="navigationTemplate" type="text/template">
<div id="navigation">
<nav class="nav-items">
<a class="item" to="/home">Home</a>
<a class="item" to="/about">About</a>
<a class="item" to="/japan">Japan</a>
</nav>
</div>
</script>
<div id="app">
<navigation />
</div>
your scroll.js can safely be written as:
window.addEventListener('scroll',
() => document.querySelector('#navigation')
.classList.toggle('colored', window.scrollY > 50)
)
Your SCSS seems incorrect:
#navigation {
.colored {
declaration
}
}
will apply declaration to any element with a class of .colored that's inside an element with the id of navigation. But your code toggles the class colored on #navigation. Therefore your SCSS should look like this:
#navigation {
&.colored {
declaration
}
}
Might not seem like much, but the & makes your code apply (or not).
You probably want to apply transition to #navigation, as it should apply when it has the colored class and when it doesn't. If you don't, the return transition (from .colored to :not(.colored)) will not be animated.
For the record and to also answer your initial question, the proper way to import custom code into a Vue component is:
a) export your code as a function:
(in scroll.js)
export function menuScroll = function() {
/* your custom code here */
}
b) import it:
(in your component)
import { menuScroll } from 'path/to/scroll'
c) run it exactly where you need it:
(i.e: in mounted)
export default {
name: 'navigation',
mounted() {
menuScroll();
}
}
Obviously, you want to rename the function in accordance with its purpose/role and the project's naming conventions.
Last, but not least, if your function needs to take params, you might want to use it as a method:
export function someName = function(...args) {
/** do stuff with args **/
}
... and, in component:
import { someName } from 'path/to/it'
export default {
name: 'whatever',
methods: {
someName,
/* more methods... */
}
}
just like that
<template>
.... your HTML
</template>
<script>
export default {
data: () => ({
......data of your component
}),
mounted() {
let recaptchaScript = document.createElement('script')
recaptchaScript.setAttribute('src', 'https://www.google.com/recaptcha/api.js')
document.head.appendChild(recaptchaScript)
},
methods: {
......methods of your component
}
}
</script>
source : Link
Related
May be somebody can help me.
I have the popup and button, when I click to create button the popup window has to open and when I click outside or on button the popup has to hide
I implement the example in sandbox (unfortunately it works locally but doesn't work in sandbox, I can't understand the reason of problem in sandbox, code base the same, I hope the example will be useful)
I implement directive:
export default {
name: 'click-outside',
mounted: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
event.stopPropagation();
if (!(el === event.target || el.contains(event.target))) {
binding.value;
}
};
document.addEventListener('click', el.clickOutsideEvent);
},
unmounted: function (el) {
document.removeEventListener('click', el.clickOutsideEvent);
},
};
simple make up:
<template>
<div class="component">
<button ref="button" #click="isDisplay = true">Create</button>
<div v-if="isDisplay" class="popup-box" v-click-outside="onClose">
Test Popup Box
</div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
isDisplay: true,
};
},
method: {
onClose() {
this.isDisplay = false;
},
},
};
</script>
<style scoped>
.component {
display: flex;
justify-content: column;
}
.popup-box {
position: absolute;
left: 50%;
top: 50px;
transform: translateX(-50%);
background: #f0f8ff;
border-radius: 1px;
box-shadow: 1px 1px 15px 0 rgba(0, 0, 0, 0.2);
padding: 40px;
color: #555585;
}
</style>
and make global the connection for directive:
import { createApp } from 'vue';
import App from './App.vue';
import ClickOutside from './directives/clickOutside.js';
const app = createApp(App);
app.directive('click-outside', ClickOutside);
app.mount('#app');
in the result Yes it works....
when I am clicking the create button 'isDisplay' set firstly as true (click event) then false (directive) and it is problem which I don't know how to fix, I tried to use ref for button but I don't clearly understand how to ingore the ref attribute in directive ( I didn't find any place where I can check ref and current click with, to understand in which place click event is triggered)
With your code base you need to assign v-click-outside on your component, or on the inner wrapper
<template>
<div class="component" v-click-outside="onClose">
<button ref="button" #click="isDisplay = true">Create</button>
<div v-if="isDisplay" class="popup-box">
Test Popup Box
</div>
</div>
</template>
And in your code you have a misspell in method field. You need to methods instead
Simply put, I want to change the color of the first two texts of each line to white, but it doesn't work anyway. It is as if the span elements generated by js cannot be affected by css.
Please see the picture for the specific code.
Sorry I'm not very good at using Stack Overflow yet, the code has been added.
export default {
mounted(){
console.log("Hello!")
let list = document.querySelectorAll('.shinchou-menu li a')
list.forEach( link => {
let letters = link.textContent.split("");
link.textContent = "";
letters.forEach((words, i) => {
let span = document.createElement("span");
span.textContent = words
if(i < 2){
span.className = "highlight"
}
span.style.transitionDelay = `${i/10}`
link.append(span);
})
})
}
}
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #fafafa;
}
</style>
<style lang="less" scoped>
.shinchou-menu {
--heightlight-text-color: #00ACF0;
list-style: none;
li {
a {
text-decoration: none;
display: inline-flex;
background: #000;
font-size: 1.6em;
font-weight: 700;
color: var(--heightlight-text-color);
padding: 4px;
margin: 6px 0;
span.highlight {
color: #FFF;
}
}
}
}
</style>
<template>
<div>
<ul class="shinchou-menu">
<li>ニュース</li>
<li>ストーリー</li>
<li>スターフ&キャスト</li>
<li>キャラクター</li>
<li>放送·配信情報</li>
</ul>
</div>
</template>
Don't manipulate DOM directly!
Vue keeps a separate DOM structure (called virtual DOM) where it tracks all elements for reactivity. Whenever something reactive changes, the DOM node in the actual DOM of the page gets re-rendered. Why? Because it's a lot faster than tracking changes in DOM.
What this means is that whenever you change DOM directly, you will lose those mods whenever Vue re-renders.
You are supposed to handle your data in the component and allow Vue to render it using template structural directives (v-if, v-for, etc...).
In your case, that would look something like this (not sure what the word separator is):
Vue2:
new Vue({
el: '#app',
data: () => ({
items: [
'ニュース',
'ストーリー',
'スターフ&キャスト',
'キャラクター',
'放送·配信情報'
]
})
})
.highlighted { color: red }
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14"></script>
<div id="app">
<ul>
<li v-for="item in items"
:class="{ highlighted: item.split('·').length > 1 }"
v-text="item" />
</ul>
</div>
Vue3:
Vue.createApp({
setup: () => ({
items: [
'ニュース',
'ストーリー',
'スターフ&キャスト',
'キャラクター',
'放送·配信情報'
]
})
}).mount('#app')
.highlighted { color: red }
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="app">
<ul>
<li v-for="item in items"
:class="{ highlighted: item.split('·').length > 1 }"
v-text="item" />
</ul>
</div>
Note: you can (and maybe should) create a method taking the item as a param and returning the correct classes for the item. This way you don't have to write your js into the template.
<li v-for="item in items" :class="itemClasses(item)">
component:
/* Options API syntax: */
methods: {
itemClasses(item) {
/* return string, array of strings or class object
docs: https://vuejs.org/guide/essentials/class-and-style.html
*/
}
}
/* (alternative) Composition API syntax : */
setup() {
// replace this function with your own logic
const itemClasses = item => item.split('·').length > 1;
return { itemClasses }
}
Iam trying to do is prevent the default state for forms in react app, in mobile view on default pull to refresh is there,
So if i accidentally do pull to refresh my page(form component) whole form value will be reset. Is there any way to prevent that component from pull to refresh, i tried with onTouchMove method and try to call event.preventDefault(),
but onTouchMove method is not firing, please let me know if there are some others methods to solve this issue, thanks in advance!.
`
class SwipeTest extends React.Component {
constructor(props) {
super(props);
this.state = {
startX: 0,
startY: 0,
currentX: 0,
currentY: 0,
direction: 'none',
threshold: 150,
}
}
touchStart(e) {
alert("i am in touchStart")
}
touchMove(e) {
alert("i am in touchMove")
}
touchEnd(e)
{
alert("i am in touchEnd")
}
render() {
return (
<div
className="w-100 bg-blue db pa4"
onTouchStart={this.touchStart.bind(this)}
onTouchMove={this.touchMove.bind(this)}
onTouchEnd={this.touchEnd.bind(this)}
>
Swipe me (up or down)
</div>
)
}
}
window.onload = () => {
ReactDOM.render(
<SwipeTest />,
document.getElementById("main")
);
};
`
I was curious about your problem so I googled a solution. The one I found basically created a wrapper around your content and with a higher z index capture the pull event and prevent it to reach your browser.
.body,
.wrapper {
/* Break the flow */
position: absolute;
top: 0px;
/* Give them all the available space */
width: 100%;
height: 100%;
/* Remove the margins if any */
margin: 0;
/* Allow them to scroll down the document */
overflow-y: hidden;
}
.body {
/* Sending body at the bottom of the stack */
z-index: 1;
}
.wrapper {
/* Making the wrapper stack above the body */
z-index: 2;
}
<!doctype html>
<html>
<head>
<!-- Put the above styles here -->
</head>
<body class="body">
<div class="wrapper">
<!-- Your content goes here -->
<div id="root" />
</div>
</body>
</html>
Here is the source of the idea: https://w3bits.com/prevent-chrome-pull-to-refresh-css/
I am working on a perfect-scrollbar directive.
yarn add perfect-scrollbar
I found this lying around, but it silently fails.
This is the code:
<template>
<div class="scroll-area">
<img src="https://i.kinja-img.com/gawker-media/image/upload/s--I5-_uX-9--/c_scale,fl_progressive,q_80,w_800/oh1cxyrtivnead6tn8k6.png" v-scroll/>
</div>
</template>
<script>
import { Container, PerfectScrollbar } from 'myCompnts';
export default {
name: 'Test',
data() {
return {
};
},
directives: {
scroll: PerfectScrollbar
},
components: {
Container
},
};
</script>
<style scoped>
.scroll-area {
position: relative;
width: 100px;
height: 300px;
overflow: hidden;
}
</style>
Unfortunately, it either "no element is specified to initialize PerfectScrollbar", or it gives back these cryptic error messages suggesting an issue with compilation:
Just as the element was not passed in correctly. Vue Chrome debugger provides no info on whether the directive actually got there. The idea sounds sexy as hell, but how to implement it? Thanks for any help, really! :)
Disclaimer: I have tried to read the docs before opening this question.
I have this component:
<template>
<accordion id="facilities-menu" :one-at-atime="true">
<template v-for="facilities in facilitiesGroups">
<panel class="accordion-toggle" :header="`Facilities #${$index+1}`" :is-open="$index === 0">
<ul>
<li v-for="facility in facilities">
{{facility}}
</li>
</ul>
</panel>
</template>
</accordion>
</template>
<style lang="scss" scoped>
#import "../../../styles/theme-colors.scss";
.panel {
background: #5E6466;
border: none;
}
</style>
<script>
import { accordion, panel } from 'vue-strap'
module.exports = {
components: {
accordion, panel
},
data () {
return {
'facilitiesGroups': [['Continente Alfragide', 'Jumbo Almada', 'Portugália'], ['Pingo Doce', 'Lidl', 'Allegro'], ['Casa']]
}
}
}
</script>
And then I instantiate this component like this:
<template>
<div class="user">
<user></user>
</div>
<facilities></facilities>
</template>
<style lang="scss" scoped>
#import "../../../styles/theme-colors";
.user {
width: 100%;
height: auto;
margin-top: 8%;
margin-bottom: 5%;
}
</style>
<script>
import User from './userProfile'
import Facilities from './groupedFacilities'
module.exports = {
components: {
'user': User,
'facilities': Facilities
}
}
</script>
However, you can see that in the first component I am defining the data to be { 'facilitiesGroups': [['Continente Alfragide', 'Jumbo Almada', 'Portugália'], ['Pingo Doce', 'Lidl', 'Allegro'], ['Casa']] }. But i want this to be passed as an argument, not to be statically defined in the component as it is now.
I have read the docs about how could this be done here. But their example...
Vue.component('child', {
// declare the props
props: ['msg'],
// the prop can be used inside templates, and will also
// be set as `this.msg`
template: '<span>{{ msg }}</span>'
})
...Resembles nothing to what I have in my code... I don't even use Vue.component() anywhere!
How come I am using a different "style" of coding with Vue JS (I started from their boilerplate)?
How can I map Vue JS official documentation to this "style"?
How can pass that array as an argument/property?
Thanks!
Your component needs to declare the data you want passed in as 'props'
<script>
import { accordion, panel } from 'vue-strap'
module.exports = {
components: {
accordion, panel
},
props: ['facilitiesGroups']
}
</script>
... then in your parent component template you pass your data as an attribute. Below is an example where "facilitiesGroups" is a data element of your parent component:
<template>
<div class="user">
<user></user>
</div>
<facilities :facilities-groups="facilitiesGroups"></facilities>
</template>