在阴影根目录中修改自定义元素的样式



我正试图使用shadow DOM修改两个自定义HTML元素的样式,即"输出屏幕"one_answers"自定义计算器"。

当我试图通过如下所示的附加阴影DOM来实现这一点时,样式不会被应用。你知道我做错了什么吗?

JS Fiddle

<custom-calculator id="calculator">
<output-screen></output-screen>
</custom-calculator>
<script>
var o = Object.create(HTMLElement.prototype);
var oElement = document.registerElement('output-screen', {
prototype: o
});
var c = Object.create(HTMLElement.prototype);
var cElement = document.registerElement('custom-calculator', {
prototype: c
});
var calc = document.querySelector('#calculator')
calc.attachShadow({ mode: 'open' });
calc.shadowRoot;
calc.shadowRoot.innerHTML = `
<style>
output-screen{
display:inline-block;
background-color:orange;
width:50%;
height:100vh;
}
custom-calculator {
display:inline-block;
background-color:grey;
width:100%;
height:100vh;
vertical-align:top;
}
</style>
`;
</script>

为了对承载Shadow DOM的元素(此处为<custom-calculator>)进行样式设置,必须使用de:host伪类(而不是Shadow DOM中未知的custom-calculator)。

:host {
display:inline-block;
background-color:grey;
width:100%;
height:100vh;
vertical-align:top;
}

因为Shadow DOM将替换/恢复普通DOM树(此处为<output-screen>),所以必须使用<slot>将其插入/显示在Shadow DOM中。

calc.shadowRoot.innerHTML = `
<style>
...
</style>
<slot></slot>`

然后,为了对<slot>元素中/显示的内容进行样式化,您必须使用::slotted()伪元素:

::slotted( output-screen ){
display:inline-block;
background-color:orange;
width:50%;
height:100vh;
}

实例:

var calc = document.querySelector('#calculator')
calc.attachShadow({mode: 'open'});
calc.shadowRoot.innerHTML = `
<style>
:host {
display:inline-block;
background-color:grey;
width:100%;
height:100vh;
vertical-align:top;
}
::slotted( output-screen ){
display:inline-block;
background-color:orange;
width:50%;
height:100vh;
}
</style>
<slot></slot>`;
<custom-calculator id="calculator">
<output-screen></output-screen>
</custom-calculator>

在使用自定义元素时,您绝对应该使用类语法,因为它会使事情变得更清楚。

以下是您提供的代码的逐步工作调整:

1.字符串模板:
您需要做的第一件事是定义将要使用的模板。

const string_templates = {
"custom-calculator": `<style>
:host {
display: inline-block;
background-color: grey;
width: 100%;
height: 100vh;
vertical-align: top;
}
</style>
<slot></slot>`, // !! slot !!
"output-screen": `<style>
:host {
display: inline-block;
background-color: orange;
width: 50%;
height: 100vh;
}
</style>`
};

这里我们声明一个关联数组,它以"key":'value'格式存储条目。我们有两个条目,它们的关键字是"custom-calculator""output-screen",每个条目都将与之相关联的模板存储为字符串值

由于<custom-calculator>元素将在其html标签中接收其他HTMLElement,我们需要在其模板中添加一个<slot>元素。该<slot>元素模板中的位置定义了<custom-selector>标记内提供的html代码将在呈现的位置。

2.从字符串模板创建最终模板:
这些模板将在创建新的自定义元素实例时使用。

const calc_template = document.createElement('template'); // creates a new <template> element
calc_template.innerHTML = string_templates["custom-calculator"]; // parse the string template only one time
const out_template = document.createElement('template');
out_template.innerHTML = string_templates["output-screen"];

在这里,我们定义了两个常量calc_templateout_template,每个常量都用一个新的空<template>元素初始化,然后添加相关联的字符串模板作为它们的innerHTML

在类外执行此操作允许在文件运行时仅解析每个模板一次。

3.类"CustomCalculator"amp"OutputScreen":
这里使用的类非常简单,因为它们只是由一个执行以下操作的构造函数组成:

  • 调用super()在使用this之前,这是必需的,因为该类是派生的(扩展另一个类),否则将出现引用错误
  • 使用element.attachShadow()将带有阴影DOMshadowRoot附加到元素
  • 克隆关联模板内容,并使用element.appendChild()将其附加为元素阴影DOM

-自定义计算器类:

// Class for the <custom-calculator> customElement
class CustomCalculator extends HTMLElement {
constructor() {
super(); // always call super() first
this.shadow = this.attachShadow( { mode:'open'} ); // attach a shadowRoot to the element
// clone a template content and add the clone as a shadow DOM child (no <template> tags)
this.shadow.appendChild( calc_template.content.cloneNode(true) ); 
} 
} //

注意:如果要从CustomCalculator内部检索<output-screen>元素,则必须使用this.querySelector()而不是this.shadow.querySelector(),因为通过<slot>提供给自定义元素html内容不属于该元件阴影DOM中的一部分,而是位于其

光DOM-OutputScreen类:

// Class for the <output-screen> customElement
class OutputScreen extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow( {mode:'open'} );
this.shadow.appendChild( out_template.content.cloneNode(true) );
} 
} //

4.注册自定义元素:
模板和用于自定义元素被定义时,我们可以使用customElements.define()注册它们。

注意:一旦在页面中定义了自定义元素标记,就不能再与另一个一起使用,因此,如果要使用两个具有相同标签的定制元素,则必须更改其中一个。

customElements.define( 'custom-calculator', CustomCalculator ); // register the <custom-calculator> customElement
customElements.define( 'output-screen', OutputScreen ); // register the <output-screen> customElement

不要忘记">s";在指令的第一部分结束时,它是错误的来源

利用率:

最简单的方法是使用相同的顺序将<script>标记内容替换为上面突出显示的代码

如果您选择将此代码保存在js文件中,即custom_calculator.js,则可以使用html:的这一行将其导入页面中

<script type="module" src="./PATH/custom_calculator.js"></script>

此时,每次在页面中放入以下html代码时,都应该使用默认样式实例化正确的自定义元素

<custom-calculator>
<output-screen></output-screen>
</custom-calculator>

修改自定义元素的默认样式:

我现在可以回答标题问题,但在进入更多细节之前,我将添加一些上下文,说明何时需要此用例。

假设您在一个项目中开发了一些自定义元素。现在正在进行一个新项目,您希望使用您已经创建了自定义元素,但它们的默认样式与新项目中使用的样式不匹配。

如何使自定义元素在样式方面更具可重用性?

您可以在下面的自定义元素类代码的主体中添加一个静态方法:

class CustomCalculator extends HTMLElement {
constructor( ) { }

static setNewStyle( css_style ) { 
let new_style = document.createElement('style'); // create a new <style> element
new_style.textContent = css_style; // set the textContent of the new <style> element as the provided style
calc_template.content.appendChild( new_style ); // insert the new <style> element at the end of the template content    
} 
} //
export { CustomCalculator }; // needed to call static methods from other files

提供的参数css_style是一个string,您可以在其中定义要应用于自定义元素css规则。然后,函数将创建一个新的<style>元素,该元素接收提供的css_style作为textContent然后将其添加为最终模板内容的最后一个子元素。

我们还需要导出自定义元素,以便能够从其他文件中使用静态方法

注意:确定性模板指的是模板构造函数实例化自定义元素

如何使用:
作为静态方法,不能从类的主体或其任何实例调用setNewStyle()方法。它是这样使用的:CustomCalculator.setNewStyle( css_style );

示例:
对于此示例,页面中唯一直接导入的JavaScript文件是index.js文件。使用以下html行导入:<script type="module" src="./index.js"></script>

我们在index.js中导入自定义元素,如下所示:

import { CustomCalculator } from './PATH/custom_calculator.js';
CustomCalculator.setNewStyle(`
:host {
background-color: black;
}
`); // when instantiated <custom-calculator> elements will now have a black background.

在第一行中,我们导入自定义元素,在第二条指令中,我们调用导入类静态方法setNewStyle( css_style ),为其提供string参数。该参数使用:host选择器将定制元素上的背景色重新定义为黑色

最新更新