JSFiddle - React, Tailwind, and code Playground

HTML

<h1> Tests : </h1>

<p>
  <strong> No Whitespace: </strong>
  <test-element></test-element>
</p>

<p>
  <strong> Single Whitespace: </strong>
  <test-element> </test-element>
</p>

<p>
  <strong> Line-Breaks: </strong>
  <test-element>

  
  </test-element>
</p>

<p>
  <strong> Slotted Text Content: </strong>
  <test-element> content </test-element>
</p>

<p>
  <strong> Slotted HTML Content: </strong>
  <test-element id="test"> <em>content</em> </test-element>
</p>

<button name="remove"> Remove Node </button>
<button name="add"> Add Node </button>

<script>
  let br = document.querySelector('button[name=remove]');
  let ba = document.querySelector('button[name=add]');
  let t = document.querySelector('#test');
  
  br.addEventListener('click', event => {
    t.removeChild(t.lastElementChild);
  });

  ba.addEventListener('click', event => {
    let n = document.createElement('pre');
    n.textContent = 'good'; t.append(n);
  });
</script>

JavaScript

class TestElement extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode : 'open' });
    this.shadowRoot.innerHTML = this.template;

    new MutationObserver(m => this.purge())
      .observe(this, { childList:true });
  }

  get template() {
    return `<slot><span>ok</span></slot>`;
  }

  purge() {
    Array.from(this.childNodes).every(c =>
			c.nodeType === Node.TEXT_NODE &&
			!c.textContent.trim()
		) && (() => {
      while (this.childNodes.length) {
        this.removeChild(this.lastChild);
      }
    })();
  }

  connectedCallback() {
    this.purge();
  }
}

customElements.define('test-element', TestElement);