cahyowhy
cahyowhy

Reputation: 563

html custom element, slot content appended after #shadow-root

i try to create custom element with js. this is my custom element

class ImageBackground extends HTMLElement {
  createdCallback() {
    let src = this.hasAttribute('src') ? this.getAttribute('src') : '/static/images/user.png'
    let className = `img-bg ${this.hasAttribute('className') ? this.getAttribute('className') : ''}`
    let isLazy = this.getAttribute('lazy') !== false

    const slotContent = document.createElement('slot')
    slotContent.setAttribute('name', 'slot-content')

    const wrapper = document.createElement('div')
    wrapper.appendChild(slotContent)
    wrapper.style.backgroundImage = `url("${src}")`
    wrapper.style.backgroundSize = 'cover'
    wrapper.style.backgroundPosition = 'center'
    wrapper.style.height = '300px'
    wrapper.setAttribute('class', className)

    this.createShadowRoot().appendChild(wrapper)
  }
}

document.registerElement('img-bg', ImageBackground)

and this is my pug template

img-bg(src="/static/images/email.svg")
      p(slot="slot-content") cek

i want to append p element inside the slot. but the p element appended after the #shadow-root.

enter image description here

can anyone solve this... :( sorry for the bad english

Upvotes: 2

Views: 2120

Answers (2)

Intervalia
Intervalia

Reputation: 10945

The <slot> tag is used to import children of the element into the <slot> and not a place for you to put your own children.

The best you could do is to wrap your <slot> in something else and then place the component's <p> tag just after the <slot>.

Alternatively, and not recommended:

If you want to add any tag, generated by the component, into the slot then you need to place the tag as a child of your element and not into the shadowDOM. As long as it is placed as a proper child, matching the requirements of the <slot> than it should show in the <slot>.

class MyEl extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode:'open'}).innerHTML = `
    <style>
      .wrapper {
        border: 1px solid black;
        padding: 5px;
      }
      ::slotted(*) {
        background-color: #8F8; 
        margin: 1px;
        padding: 5px;
      }
    </style>
    <p>Before Slot</p>
    <div class="wrapper"><slot>Hi There</slot></div>
    <p>After Slot</p>
    `;
  }

  connectedCallback() {
  }
}

customElements.define('my-el', MyEl);

function addChild() {
  var el = document.querySelector('my-el');
  var p = document.createElement('p');
  p.textContent = "New stuff as a child.";
  el.appendChild(p);
}

var button = document.querySelector('#one');
button.addEventListener('click', addChild);
p {
  font-family: tahoma;
}

my-el p {
  font-weight: bold;
}
<p>Before Element</p>
<my-el>
  <p>Stuff in the slot</p>
</my-el>
<p>After Element</p>
<hr/>
<button id="one">Add Child</button>

So the only way to add into the slot is to add child elements to the element.

That is probably not a good thing to do since you are altering the content that the user of your component created. And that could mess up their code, CSS or both.

Upvotes: 0

Supersharp
Supersharp

Reputation: 31171

<slot> is defined in Shadow DOM v1. Therefore you mus use attachShadow() instead of createShadowRoot():

this.attachShadow({ mode:'open'}).appendChild(wrapper)

Upvotes: 1

Related Questions