Renato Britto Araujo
Renato Britto Araujo

Reputation: 43

How to include a local JS file into Vue template

I want to import a JS file to be run along with a template in browser. I tried this, but it didn't work because I need everything loaded before my script can run.

Let me show you the problematic vue file:

<template>
    <div id="canvaspage">

        <canvas id="canvas"></canvas>

        <div id="buttonlist">
            <h5>Select your action:</h5>
            <div class="col">
                <button id="btn1">JS file custom action 1</button>
                <button id="btn2">JS file custom action 2</button>
            </div>
        </div>

    </div>
</template>

<script>
export default {
    name: 'CanvasPage'
}
</script>
...

See that canvas and buttons on template? I want to interact with it using pure JS.

Here is an example of what the JS file is trying to do:

let canvas = document.getElementById('canvas')
let button1 = document.getElementById('btn1')
let button2 = document.getElementById('btn2')

canvas.addEventListener('click', () => {
    console.log('Canvas clicked')
})

button1.addEventListener('click', () => {
    console.log('Button 1 clicked')
})

button2.addEventListener('click', () => {
    console.log('Button 2 clicked')
})

If I try the solution linked above, what happens is that 'canvas', 'button1' and 'button2' are all null, because JS cannot find them. How can I make it work on Vue?

Upvotes: 2

Views: 2723

Answers (2)

Maciej Kwas
Maciej Kwas

Reputation: 6419

I don't see a reason- in this example- why you want to do anything in external js file, why not just interact with dom the vue way - I mean, proper way? Vue can destroy or replace your element with any v-if or rerender action. You can always link to your elements with this.$refs if you want to interact with DOM directly which is lots better than querySelector thingy. But anyway, here's a dummy example:

// external js file - ./extfile.js
export function canvasClick(...args) {
    console.log('canvas clicked with: ', args);
}
export function button1Click(...args) {
    console.log('button1 clicked with: ', args);
}
export function button2Click(...args) {
    console.log('button2 clicked with: ', args);
}

// vue component
<template>
    <div id="canvaspage">
        <canvas id="canvas" @click="canvasAction"></canvas>
        <div id="buttonlist">
            <h5>Select your action:</h5>
            <div class="col">
                <button id="btn1" @click.prevent="button1Action">JS file custom action 1</button>
                <button id="btn2" @click.prevent="button2Action">JS file custom action 2</button>
            </div>
        </div>
    </div>
</template>

<script>
import { canvasClick, button1Click, button2Click } from './extfile';
export default {
    name: 'CanvasPage',
    methods: {
        canvasAction(event) { canvasClick(event, this) },
        button1Action(event) { button1Click(event, this) },
        button2Action(event) { button2Click(event, this) },
    }
}
</script>

Upvotes: 1

acdcjunior
acdcjunior

Reputation: 135742

Objects managed by Vue are create/destroyed according to Vue' lifecycle. This means that any external code you use to query vue-managed elements should be somewhat coupled to Vue's lifecycle.

This means that, ideally, you should use Vue itself to add the behaviour you want. You should, for instance, add this new function you want into a Vue component. This guarantees a simpler design.

Alternative: If the Vue components are from third-parties, perhaps from another team which you can't count on, you could hook those event listeners to the document and check the target's id attribute instead of hooking the event listeners directly to the canvas element (which may be destroyed by Vue and the hooks lost).

document.body.addEventListener('click', (event) => {
  switch (event.target.id) {
    case 'canvas':
      console.log('Canvas clicked');
      break;
    case 'btn1':
      console.log('Button 1 clicked');
      break;
    case 'btn2':
      console.log('Button 2 clicked');
      break;
  }
}, true);

This code makes it very obvious that if you have more than one element in the DOM with those IDs, all of them will trigger the code.

Demo:

const CanvasComponent = Vue.component('canvas-component', {
  template: `#canvas-component`,
});
const BlankComponent = Vue.component('blank-component', {
  template: `<div><h3>Now click back to canvas and see that the listeners still work.</h3></div>`,
});

var router = new VueRouter({
  routes: [{
      path: '/',
      component: {template: '<div>Click one link above</div>'}
    },{
      path: '/blank',
      component: BlankComponent,
      name: 'blank'
    },
    {
      path: '/canvas',
      component: CanvasComponent,
      name: 'canvas'
    }
  ]
});

var app = new Vue({
  el: '#app',
  router: router,
  template: `
  <div>
     <router-link :to="{name: 'canvas'}">canvas</router-link> |
     <router-link :to="{name: 'blank'}">blank</router-link>
     
     <router-view></router-view>
  </div>
  `
});


document.body.addEventListener('click', (event) => {
  switch (event.target.id) {
    case 'canvas':
      console.log('Canvas clicked');
      break;
    case 'btn1':
      console.log('Button 1 clicked');
      break;
    case 'btn2':
      console.log('Button 2 clicked');
      break;
  }
}, true);
<script src="//unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="//unpkg.com/[email protected]/dist/vue-router.min.js"></script>

<div id="app">
  <canvas-component></canvas-component>
</div>

<template id="canvas-component">
  <div id="canvaspage">
    <canvas id="canvas"></canvas>
    <div id="buttonlist">
      <h5>Select your action:</h5>
      <div class="col">
        <button id="btn1">JS file custom action 1</button>
        <button id="btn2">JS file custom action 2</button>
      </div>
    </div>
  </div>
</template>

Upvotes: 0

Related Questions