为什么视频标签未在此视频播放器 Web 组件中加载?

发布于 2025-01-09 11:08:00 字数 1190 浏览 2 评论 0原文

我正在尝试构建一个视频播放器 Web 组件,但我似乎无法正确渲染 videosource 元素。

customElements.define('video-player',
  class extends HTMLElement {
    constructor() {
      super();
      const template = document.getElementById('video-player-template').content;
      console.log(template)
      const shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(template.cloneNode(true));
    }
  }
);
<template id="video-player-template">
    <video controls width="720" height="380" muted autoplay>
        <slot name="video-src" />
    </video>
    <slot></slot>
</template>


<video-player>
    <h1>Video player web component</h1>
    <source slot="video-src" src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm" type="video/webm" />
</video-player>

为什么 video 元素不渲染?

I'm trying to build a video player Web Component, but I can't seem to get the video and source elements to render properly.

customElements.define('video-player',
  class extends HTMLElement {
    constructor() {
      super();
      const template = document.getElementById('video-player-template').content;
      console.log(template)
      const shadowRoot = this.attachShadow({mode: 'open'});
      shadowRoot.appendChild(template.cloneNode(true));
    }
  }
);
<template id="video-player-template">
    <video controls width="720" height="380" muted autoplay>
        <slot name="video-src" />
    </video>
    <slot></slot>
</template>


<video-player>
    <h1>Video player web component</h1>
    <source slot="video-src" src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm" type="video/webm" />
</video-player>

Why doesn't the video element render?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

围归者 2025-01-16 11:08:01

请参阅文档: https://developer.mozilla.org/ en-US/docs/Web/HTML/Element/video

想要一个 元素作为立即 孩子,
因此你不能在那里使用
也是如此,它不能有

您从 src 中提取 src > Web 组件,
然后创建标记。

我已经添加了完整示例的所有 ShadowDOM 样式选项

customElements.define('video-player',
  class extends HTMLElement {
    constructor() {
      super()
        .attachShadow({mode:'open'})
        .append(document.getElementById(this.nodeName).content.cloneNode(true));
    }
    connectedCallback() {
      let src = this.getAttribute("source");
      let ext = src.split(".").slice(-1)[0];
      this.shadowRoot
          .querySelector("video")
          .innerHTML = `<source src="${src}" type="video/${ext}">`;
    }
  }
);
<video-player source="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm">
  <span slot="title">A beautiful video</span>
  <div class="desc">My video description</div>
</video-player>
<video-player source="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm">
  <span slot="title">Another beautiful video</span>
  <div class="desc">And more description</div>
</video-player>

<template id="VIDEO-PLAYER">
  <style>
    :host { display:inline-block }
    h1 { margin:0px;background:var(--bgcolor,green);text-align:center }
  </style>
  <div part="videoContainer">
    <h1><slot name="title"></slot></h1>
    <video controls width="100%" muted></video>
    <div><slot><!-- all non-slot labeled content goes here --></slot></div>
  </div>
</template>

<style>
  video-player {
    font: 10px Arial; /* Inheritable styles style shadowDOM */
    width: 240px;
    --bgcolor: gold; /* CSS properties can style shadowDOM */
  }
  .desc { /* container/global CSS styles slotted content!!!!  */
    width: 100%;
    background: beige;
  }
  ::part(videoContainer){ /* shadowParts style all usages in shadowDOM */
    border: 5px solid grey;
  }
</style>

ShadowDOM 的样式如下:

see the documentation: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video

<video> wants a <source> element as immediate child,
thus you can't use a <slot> there.
(the same is true for <table>, which can't have <slot>)

You extract the src from your <video-player src="..."> Web Component,
then create that <source> tag.

I have added all shadowDOM styling options for a complete example

customElements.define('video-player',
  class extends HTMLElement {
    constructor() {
      super()
        .attachShadow({mode:'open'})
        .append(document.getElementById(this.nodeName).content.cloneNode(true));
    }
    connectedCallback() {
      let src = this.getAttribute("source");
      let ext = src.split(".").slice(-1)[0];
      this.shadowRoot
          .querySelector("video")
          .innerHTML = `<source src="${src}" type="video/${ext}">`;
    }
  }
);
<video-player source="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm">
  <span slot="title">A beautiful video</span>
  <div class="desc">My video description</div>
</video-player>
<video-player source="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm">
  <span slot="title">Another beautiful video</span>
  <div class="desc">And more description</div>
</video-player>

<template id="VIDEO-PLAYER">
  <style>
    :host { display:inline-block }
    h1 { margin:0px;background:var(--bgcolor,green);text-align:center }
  </style>
  <div part="videoContainer">
    <h1><slot name="title"></slot></h1>
    <video controls width="100%" muted></video>
    <div><slot><!-- all non-slot labeled content goes here --></slot></div>
  </div>
</template>

<style>
  video-player {
    font: 10px Arial; /* Inheritable styles style shadowDOM */
    width: 240px;
    --bgcolor: gold; /* CSS properties can style shadowDOM */
  }
  .desc { /* container/global CSS styles slotted content!!!!  */
    width: 100%;
    background: beige;
  }
  ::part(videoContainer){ /* shadowParts style all usages in shadowDOM */
    border: 5px solid grey;
  }
</style>

ShadowDOM is styled by:

笙痞 2025-01-16 11:08:01

使用 observedAttributes 提取设置值的方法略有不同......

逻辑如下:

  • 创建一个基本的 标签
  • 每次重新使用该组件作为 标签
  • 视频标签值是从 标签中提取的。 的标签设置代码。
  • 使用从 中提取的值动态创建 标记对象。

这是一些可测试的代码:

<html>
<head>
<style>

</style>
</head>
<body>

<!-- 1) create template -->
<template id="video-player-template">

<slot></slot>

</template>

<!-- 2) test as Component -->
<!-- test Component #1 with video loop -->
<video-player id="vidplayer1" width="400" height="300" muted autoplay controls loop type="video/webm" 
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm" >
</video-player>

<!-- test Component #2  -->
<video-player id="vidplayer2" width="200" height="120" muted autoplay controls type="video/webm" 
src="https://www.w3schools.com/tags/movie.mp4" >
</video-player>

<!-- controller scripts -->

<script type="text/javascript">

/*
test files
> MP4:  https://www.w3schools.com/tags/movie.mp4
> WEBM: https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
*/

var template;
var nodes;
var shadowRoot;

customElements.define(
    'video-player',
    class extends HTMLElement 
    {
        constructor() 
        {
            super();
            template = document.getElementById('video-player-template').content;

            console.log(template)

            shadowRoot = this.attachShadow({mode: 'open'});
            shadowRoot.appendChild(template.cloneNode(true));
        }
        
        //# get component attributes (from template tag setup)
        static get observedAttributes() 
        {
            //# extract values from video tag template to use in output video tag
            //# eg:  controls id width height muted loop autoplay ... etc
            return ['src', 'id', 'width', 'height', 'controls', 'muted', 'autoplay', 'loop'];
        }

        //# attribute change
        attributeChangedCallback(property, oldValue, newValue) 
        {
            if (oldValue === newValue) { return; }
            else { this[ property ] = newValue; }
        }

        //# connect component
        connectedCallback() 
        {

            //# component is ready to be accessed

            //# generate dynamic video tag
            let player_code = "";
            player_code += `<video `;

            if( `${ this.id }` != "undefined")
            { player_code += `id="${ this.id }" `}

            if( `${ this.width }` != "undefined")
            { player_code += `width="${ this.width }" `; }

            if( `${ this.height }` != "undefined")
            { player_code += `height="${ this.height }" `; }

            if( `${ this.controls }` != "undefined")
            { player_code += `controls `; }

            if( `${ this.muted }` != "undefined")
            { player_code += `muted `; }

            if( `${ this.autoplay }` != "undefined")
            { player_code += `autoplay `; }

            if( `${ this.loop }` != "undefined")
            { player_code += `loop `; }
            
            player_code += `<source src="${ this.src }" `;
            
            //# get TYPE for video ( because ".type" is a reserved keyword )
            if( String((`${ this.src }`).indexOf(".webm")) != -1)
            { player_code += `type="video/webm" `; }
            else if( String((`${ this.src }`).indexOf(".mp4")) != -1)
            { player_code += `type="video/mp4" `; }

            player_code += `/> </video> `;

            //# apply code of dynamic video tag (add to page)...
            this.innerHTML = player_code;
            
    }
});

</script>
</body>
</html>

A slightly different approach using observedAttributes to extract setup values...

The logic is like this:

  • Create a basic <template id="video-player-template"> </template> tag
  • Re-use the component each time as a <video-player> tag
  • The video tag values are extracted from the <video-player>'s tag setup code.
  • Dynamically create a <video> tag object with extracted values from <video-player>.

Here is some testable code:

<html>
<head>
<style>

</style>
</head>
<body>

<!-- 1) create template -->
<template id="video-player-template">

<slot></slot>

</template>

<!-- 2) test as Component -->
<!-- test Component #1 with video loop -->
<video-player id="vidplayer1" width="400" height="300" muted autoplay controls loop type="video/webm" 
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm" >
</video-player>

<!-- test Component #2  -->
<video-player id="vidplayer2" width="200" height="120" muted autoplay controls type="video/webm" 
src="https://www.w3schools.com/tags/movie.mp4" >
</video-player>

<!-- controller scripts -->

<script type="text/javascript">

/*
test files
> MP4:  https://www.w3schools.com/tags/movie.mp4
> WEBM: https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
*/

var template;
var nodes;
var shadowRoot;

customElements.define(
    'video-player',
    class extends HTMLElement 
    {
        constructor() 
        {
            super();
            template = document.getElementById('video-player-template').content;

            console.log(template)

            shadowRoot = this.attachShadow({mode: 'open'});
            shadowRoot.appendChild(template.cloneNode(true));
        }
        
        //# get component attributes (from template tag setup)
        static get observedAttributes() 
        {
            //# extract values from video tag template to use in output video tag
            //# eg:  controls id width height muted loop autoplay ... etc
            return ['src', 'id', 'width', 'height', 'controls', 'muted', 'autoplay', 'loop'];
        }

        //# attribute change
        attributeChangedCallback(property, oldValue, newValue) 
        {
            if (oldValue === newValue) { return; }
            else { this[ property ] = newValue; }
        }

        //# connect component
        connectedCallback() 
        {

            //# component is ready to be accessed

            //# generate dynamic video tag
            let player_code = "";
            player_code += `<video `;

            if( `${ this.id }` != "undefined")
            { player_code += `id="${ this.id }" `}

            if( `${ this.width }` != "undefined")
            { player_code += `width="${ this.width }" `; }

            if( `${ this.height }` != "undefined")
            { player_code += `height="${ this.height }" `; }

            if( `${ this.controls }` != "undefined")
            { player_code += `controls `; }

            if( `${ this.muted }` != "undefined")
            { player_code += `muted `; }

            if( `${ this.autoplay }` != "undefined")
            { player_code += `autoplay `; }

            if( `${ this.loop }` != "undefined")
            { player_code += `loop `; }
            
            player_code += `<source src="${ this.src }" `;
            
            //# get TYPE for video ( because ".type" is a reserved keyword )
            if( String((`${ this.src }`).indexOf(".webm")) != -1)
            { player_code += `type="video/webm" `; }
            else if( String((`${ this.src }`).indexOf(".mp4")) != -1)
            { player_code += `type="video/mp4" `; }

            player_code += `/> </video> `;

            //# apply code of dynamic video tag (add to page)...
            this.innerHTML = player_code;
            
    }
});

</script>
</body>
</html>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文