Search code examples
javascriptdomweb-componentshadow-domcustom-element

How to find the parent element width using javascript when we are using slot


I have one custom element having one div that contains slot.

In that slot, I have JS script where i want to get width of slot's parent div.

I know words are every much confusing :).

here is the sample code link

<!doctype html>
<body>
<script>
customElements.define('user-card', class extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = `
      <style>
        .name {
          font-size: 24px;
          width:200px;
          background-color: yellow
        }
      </style>
      <div class="name">Name:
        <slot name="username"></slot>
      </div>
      <div class="dob">Birthday:
        <slot name="birthday"></slot>
      </div>
    `;
    this.shadowRoot.styles = `

    `
  }
});
</script>

<user-card>
  <span slot="username">
    <script>
      function abc(event) {
        console.log('Expected: ', this.document.getElementsByTagName('user-card')[0].shadowRoot.querySelectorAll('div.name')[0].offsetWidth)
        console.log('Result: ',event.target.parentElement.offsetWidth);
      }

    </script>
    <div onclick="abc(event)">
      Ritesh Rajput
      </div>

  </span>
  <span slot="birthday">01.01.2001</span>
</user-card>
</body>

ideally both should return same value

console.log('Expected: ', this.document.getElementsByTagName('user-card')[0].shadowRoot.querySelectorAll('div.name')[0].offsetWidth)
console.log('Result: ',event.target.parentElement.offsetWidth);

Solution

  • In the second log the element event.target.parentElement is the <span slot="username">. A <span> is inline and has a width of 0.

    You'll had to set the CSS display property to inline-block if you want to read its width.

    You can define it in the main page:

    <head>
      <style>
        span[slot=username] {
          display: inline-block;
          width: 100%; 
        }
      </style>
    </head>
    

    Or in the Shadow DOM via the ::slotted() CSS pseudo-element:

    <style>
      ::slotted(span[slot=username]) {
          display: inline-block;
          width: 100%;         
      }
    </style>
    

    customElements.define('user-card', class extends HTMLElement {
      connectedCallback() {
        this.attachShadow({mode: 'open'})
            .innerHTML = `
          <style>
            div.name {
              font-size: 24px;
              width: 200px;
              background-color: yellow;
              display: inline-block;
            }
            ::slotted(span[slot="username"]) {
              display: inline-block; 
              width: 100%;
            }
          </style>
          <div class="name">Name :
            <slot name="username"></slot>
          </div>
          <div class="dob">Birthday:
            <slot name="birthday"></slot>
          </div>
        `
      }
    })
    
    function abc(event) {
      console.log('Expected: ', this.document.getElementsByTagName('user-card')[0].shadowRoot.querySelector('.name').offsetWidth)
      console.log('Result: ', event.target.parentElement.offsetWidth)
    }
    <user-card>
      <span slot="username">
        <div onclick="abc(event)">Ritesh Rajput</div>
      </span>
      <span slot="birthday">01.01.2001</span>
    </user-card>