15

I have a shadow dom which contains the root element and a vue component.

<template>
    <div class="container">
        <div id="app"></div>
    </div>
    <script src="http://my-site.com/app/js/vue-component.js"></script>
</template>

<div id="hostElement"></div>
<script>
// create shadow DOM on the <p> element above
const shadow = document.querySelector('#hostElement').attachShadow({mode: 'open'});
const template = document.querySelector('template');
shadow.appendChild(document.importNode(template.content, true));
</script>

Inside the vue-component.js looks something like this:

import Vue from 'vue';

const shadow = document.querySelector('#hostElement').shadowRoot;

new Vue({
    el: shadow.querySelector('#app'),
    // ...
})

// this doesn't work because I think Vue is using document.querySelector('#app')
// instead of the required document.querySelector('#hostElement').shadowRoot.querySelector('#app')
// new Vue ({
//     el: '#app'
// })

Everything works fine if I use this stuff outside the shadow dom (like regular people do). However, it seems like Vue isn't able to handle shadow dom stuff. I believe it shouldn't be calling document.querySelector if it's inside the shadow dom. Instead, it should be running shadowRoot.querySelector.

Please let me know if this is confusing at all, I'm in a uncommon use case scenario so it is a little hard to explain.

Jeff
  • 2,018
  • 2
  • 15
  • 18

1 Answers1

16

---Update---

If you pass a reference to an element instead of a selector, Vue will use the element.

let element = document.querySelector('#host-element').shadowRoot.querySelector('#your-future-vue-component');
new Vue({
  el: element,
  ...
})

---Old Stuff---

I have half a solution for you using vue-custom-element. I say half, because it puts your vue component into a webcomponent and gives you the option to have it use the shadow DOM. This means that the Vue Custom element will also have to be your shadowRoot. Hopefully this suits your needs.

https://github.com/karol-f/vue-custom-element

import Vue from 'vue';
import vueCustomElement from 'vue-custom-element';
import app from './app.vue';

Vue.use(vueCustomElement);
Vue.customElement("my-element", app, {shadow: true});
myShadowElement = document.createElement('my-element');
document.body.appendChild(myShadowElement);

The only other option I think you have is to go modify Vue's code. You could do so and create a pull request, or just hack it for yourself.

SethWhite
  • 1,891
  • 18
  • 24
  • what's the syntax for getting shadow Dom closed mode? – qwerty_igor Sep 13 '18 at 18:19
  • 2
    @qwerty_igor I'm a little fuzzy on this, but from https://blog.revillweb.com/open-vs-closed-shadow-dom-9f3d7427d1af it looks like you need to store a reference when you create your shadow element, like so `const shadowRoot = this.attachShadow({ mode: "closed" })`, then pass shadowRoot to the `el` in your vue component. – SethWhite Sep 13 '18 at 22:02
  • 1
    Thanks for responding. Yes, it wouldn't work anyways, as the repo is checking existence of shadowRoot prop and in closed mode it returns null, so it will not render in closed mode. Thanks for pointing me to attachShadow function. – qwerty_igor Sep 13 '18 at 22:30
  • 2
    use `{mode: "open"}` there's no practical benefit to `mode: "closed"`--this allows referencing it anytime from the `shadowRoot` property, eg `anyCustomElement.shadowRoot` – jimmont Nov 27 '19 at 19:44
  • this makes the most sense of eveyrthing I've seen, but what is #your-future-vue-component ? is that the name of the component, the id of where the component is inserted, or something else? – ToddT Apr 22 '22 at 15:13
  • 1
    @ToddT its the css selector for the element in the Shadow Dom where the vue component will be mounted – SethWhite Apr 23 '22 at 16:48