【问题标题】:Web Component: Accessing the shadow DOM within the Custom Component classWeb 组件:访问自定义组件类中的影子 DOM
【发布时间】:2018-09-11 04:42:32
【问题描述】:

在这个项目中,我正在尝试创建一个自定义组件,它的行为类似于地图。我仍在通过 W3C 文档和几个 Youtube 视频学习 Web 组件的基础知识。

主要问题是,在组件类中有一个名为attributeChangedCallback() 的函数,每次属性更改时都会触发,这些属性包含在observedAttributes() 函数中,并且在设置它们之后(前面提到的函数)我尝试通过选择器访问影子 DOM(声明为connectedCallback(),一旦组件加载到 HTML body 元素中就会触发)。但是,如果我这样做,变量内容为空。

attributeChangedCallback() 应该被加载之后内容被加载,所以我不明白为什么会发生这种情况,每次属性更改时我都需要访问这个元素,以便我可以更新它内容。奇怪的事实:如果我每次执行attributeChangedCallback() 时都会记录两次(因为我有两个属性被监视)。

这是sn-p:

class GeoComponent extends HTMLElement {
	constructor() {
		super();
		this.shadow = this.createShadowRoot();
		this._latitude = 0;
		this._longitude = 0;
	}

	get latitude(){
		return this._latitude;
	}

	get longitude(){
		return this._longitude;
	}

	set latitude(val){
		this.setAttribute('latitude', val);
	}

	set longitude(val){
		this.setAttribute('longitude', val);
	}

	static get observedAttributes(){
		return ['latitude', 'longitude'];
	}
	
	attributeChangedCallback(name, oldVal, newVal){
		let geoComp = this.shadow.getElementById('geo');
		console.log(geoComp);
		switch(name){
			case 'latitude':
				this._latitude = parseInt(newVal, 0) || 0;
				// geoComp.innerHTML = `${this.latitude}, ${this.longitude}`;
				break;
			case 'longitude':
				this._longitude = parseInt(newVal, 0) || 0;
				// geoComp.innerHTML = `${this.latitude}, ${this.longitude}`;
				break
		}
	}

	connectedCallback(){
		let template = `
			<div class="geo-component" id="geo">
				
			</div>
		`;

		this.shadow.innerHTML = template;
	}
}

window.customElements.define('geo-component', GeoComponent);
<!DOCTYPE html>
<html>
<head>
	<title></title>
	<script src="geoComponent.js"></script>
</head>
<body>
	<h1>Geo-Component</h1>
	<geo-component latitude="12" longitude="-70"></geo-component>
</body>
</html>

更新

就像@acdcjunior 在将this.createShadowRoot(); 更改为this.shadow = this.attachShadow({mode: 'open'});(从ShadowDOM v0 到v1)后提到的那样解决了我的问题,因为connectedCallback() 函数在后台执行并且只执行一次。

【问题讨论】:

    标签: javascript web-component web-component-tester google-web-component native-web-component


    【解决方案1】:

    attributeChangedCallback() 在(观察到的)属性初始化(以声明方式,这是您的情况)、添加、更改或删除时调用。这意味着它在connectedCallback() 之前被调用。

    通常我们使用 constructor() 来创建 DOM:

    名称: 构造函数
    调用时间:创建元素的实例 或升级。用于初始化状态,设置事件 听众,或创建 shadow dom。有关限制,请参阅规范 你可以在构造函数中做什么。

    我将你的逻辑移到它上面。

    另外,您使用的是 ShadowDOM v0。将其更新为 v1(attachShadow 而不是 createShadowRoot)。

    更新的演示:

    class GeoComponent extends HTMLElement {
      constructor() {
        super();
        console.log('constructor called');
        
        this.attachShadow({mode: 'open'});
        this._latitude = 0;
        this._longitude = 0;
        
        let template = `
    			<div class="geo-component" id="geo">
    
    			</div>
    		`;
        this.shadowRoot.innerHTML = template;
      }
    
      get latitude() {
        return this._latitude;
      }
    
      get longitude() {
        return this._longitude;
      }
    
      set latitude(val) {
        this.setAttribute('latitude', val);
      }
    
      set longitude(val) {
        this.setAttribute('longitude', val);
      }
    
      static get observedAttributes() {
        return ['latitude', 'longitude'];
      }
    
      attributeChangedCallback(name, oldVal, newVal) {
        console.log('attributeChangedCallback() called:', name, ':', oldVal, '->', newVal);
    
        let geoComp = this.shadowRoot.getElementById('geo');
        console.log(geoComp);
        
        switch (name) {
          case 'latitude':
            this._latitude = parseInt(newVal, 0) || 0;
            // geoComp.innerHTML = `${this.latitude}, ${this.longitude}`;
            break;
          case 'longitude':
            this._longitude = parseInt(newVal, 0) || 0;
            // geoComp.innerHTML = `${this.latitude}, ${this.longitude}`;
            break
        }
      }
    
      connectedCallback() {
        console.log('connectedCallback() called');
      }
    }
    
    window.customElements.define('geo-component', GeoComponent);
    <!DOCTYPE html>
    <html>
    
    <head>
      <title></title>
      <script src="geoComponent.js"></script>
    </head>
    
    <body>
      <h1>Geo-Component</h1>
      <geo-component latitude="12" longitude="-70"></geo-component>
    </body>
    
    </html>

    【讨论】:

    • 哦,我不知道我使用的是旧版本的 ShadowDOM。这解决了我的问题,谢谢。
    • 您可以访问元素本身的shadowRoot,即this.shadowRoot。见:developer.mozilla.org/en-US/docs/Web/API/Element/shadowRoot
    • @holmberd 这一定是新事物吧?
    • @holmberd 很好,不是那么新。我已将其添加到答案中。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 2014-12-09
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    相关资源
    最近更新 更多