1

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

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

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

这是片段:

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()函数在幕后执行并且只执行一次。

4

1 回答 1

2

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>

于 2018-04-01T23:05:39.210 回答