10

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;
    }
    
}
DevThiman
  • 920
  • 1
  • 9
  • 24
GLaDOS
  • 111
  • 1
  • 1
  • 3

2 Answers2

19

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>

  1. your scroll.js can safely be written as:
window.addEventListener('scroll',
  () => document.querySelector('#navigation')
  .classList.toggle('colored', window.scrollY > 50)
)
  1. 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).

  1. 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... */
  }
}
tony19
  • 125,647
  • 18
  • 229
  • 307
tao
  • 82,996
  • 16
  • 114
  • 150
  • Thank you for your reply. In which files shall I insert the code snippet block 1 and 3? I think, the first code block from your code snippet has to be insertet in Navigation.vue, is that right? And the third code-block looks like it should be in App.vue, but the div container should be inside the script-tag, but the template-tag. It’s a but confusing. – GLaDOS Jun 02 '19 at 02:13
  • In `navigation.vue`, don't import `scroll.js`. Instead, add a `mounted() {}` under `name`, inside `export`. Inside `{}` of mounted place the `scroll.js` code (or my replacement). In `navigation.scss`, add a `&` before `.colored`. And move the `transition` rule outside of `&.colored` (into `#navigation`). Unless I'm missing something, it should work as my example. If you need more help, try to replicate your issue in a [codesandbox](https://codesandbox.io) and I'll take a look. Make sure the issue is reproduced, please. – tao Jun 02 '19 at 02:18
  • 1
    If you really want `scroll.js` in its separate file, the proper way to do it is: wrap it inside `export function menuScroll = function() { /* your old scroll.js code here */ }` and import it as `import { menuScroll } from 'path/to/scroll.js'`. Note `import` must be placed before `export`!. And, the mounted hook should look like: `mounted() { menuScroll() }`. That's it. – tao Jun 02 '19 at 02:31
4

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

Pooya Panahandeh
  • 578
  • 7
  • 21