我正在尝试构建一个视频播放器Web组件,但似乎无法正确渲染video
和source
元素。
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
元素不呈现?
请参阅文档:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video
<video>
希望<source>
元素作为立即子元素,
因此不能在那里使用<slot>
(<table>
也是如此,不能有<slot>
(
从<video-player src="...">
Web组件中提取src
,
然后创建该<source>
标记。
我已经为一个完整的示例添加了所有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的样式为:
-
阴影DOM 内的
<style>
-
可继承样式
https://lamplightdev.com/blog/2019/03/26/why-is-my-web-component-inheriting-styles/ -
(级联(CSS属性
-
shadowParts(和主题(
https://meowni.ca/posts/part-theme-explainer/ -
<slot>
是反射的,它们不是由shadowDOM设计的,而是由其容器设计的
请参阅::分时段内容 -
(2022年2月(Constructable StyleSheets仍然是Chromium的专属派对
https://caniuse.com/mdn-api_cssstylesheet_cssstylesheet
使用observedAttributes提取设置值的方法略有不同。。。
逻辑是这样的:
- 创建一个基本的
<template id="video-player-template"> </template>
标记 - 每次将组件重新用作
<video-player>
标签 - 视频标签值是从
<video-player>
的标签设置代码中提取的 - 使用从
<video-player>
中提取的值动态创建<video>
标记对象
下面是一些可测试的代码:
<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>