Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue: Bind click event to dynamically inserted content

I have a Vue app which requests some html from an API that contains some blocks like <div class="play-video">...</div>

Calling the API with axios via a promise, it is inserted into the dom something like:

<div v-if="content" v-html="content"></div>

How can I bind click events to the children with the .play-video class? Is it just a matter of watching for content to change, and then running vanilla js query selectors to bind to them?

like image 440
waffl Avatar asked Dec 20 '18 23:12

waffl


1 Answers

You can use some event delegation by adding a @click handler and checking the target element using Element.closest()

new Vue({
  el: "#app",
  data: () => ({
    content: null
  }),
  methods: {
    handleClick(e) {
      const elt = e.target.closest(".play-video");
      if (elt) {
        console.log("Got a click on .play-video or a child element")
      }
    }
  },
  mounted () {
    // simulate loading content
    setTimeout(() => {
      this.content = `
        <div>
          <p>Some other element</p>
          <button>I'm not part of <code>.play-video</code></button>
          <div class="play-video">
            <p><button>Click me, I am!</button></p>
          </div>
        </div>
        `
    }, 1000)
  }
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
<div id="app">
  <div v-if="content" v-html="content" @click="handleClick"></div>
  <p v-else>Loading...</p>
</div>

If you only want to catch click events on the .play-video element itself and not any of its children, skip the .closest() call and use

if (e.target.matches(".play-video") {
  // ...
}

See https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#polyfill for browser compatibility and a polyfill if required.

like image 51
Phil Avatar answered Nov 06 '22 23:11

Phil