少し複雑なカスタムタグ

カスタムタグに属性を追加し、クリック時のイベントや属性の変更と連動させるサンプルです。
次のような構成のカスタムタグを作成します。

質問です

ソースコード

とりあえずソースコードは次のようになります。

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>WebContent Sample</title>
</head>
<body>
	<!--
	(参考)
	https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=ja
	https://html5experts.jp/shumpei-shiraishi/24239/
	-->
	<script>
	class QuestionBox extends HTMLElement {
		//*********************************************************************
		// Static Methods
		//*********************************************************************
		static get template(){
			return `
				<div id="question-str" class="question-cell">
					<slot></slot>
				</div>
				<div class="question-cell">
					<input type="radio" name="rd-question" value="0">
					<input type="radio" name="rd-question" value="1">
					<input type="radio" name="rd-question" value="2">
					<input type="radio" name="rd-question" value="3">
					<input type="radio" name="rd-question" value="4">
				</div>
				<style>
				.question-cell {
					display: table-cell;
				}
				</style>
			`;
		}

		//*********************************************************************
		// Member Methods
		//*********************************************************************
		constructor(){
			super();
		}

		connectedCallback(){
			let sr = this.attachShadow({mode: "open"});
			sr.innerHTML = QuestionBox.template;

			this.style.display = "table-row";
			this.defaultCheck();
		}

		defaultCheck(){
			let ipts = this.shadowRoot.querySelectorAll("input");
			let val = this.getAttribute("value");
			ipts[val].checked = true;
		}

		get value(){
			const ipts = this.shadowRoot.querySelectorAll("input");
			for(let i=0; i<ipts.length; i++){
				if(ipts[i].checked){
					return parseInt(ipts[i].value);
				}
			}
		}

		set value(val){
			let ipts = this.shadowRoot.querySelectorAll("input");
			this.setAttribute("value", val);
			for(let i=0; i<ipts.length; i++){
				if(parseInt(ipts[i].value) == val){
					ipts[i].checked = true;
				}
			}
		}
	}

	customElements.define('question-box', QuestionBox);
	</script>

	<div style="display: table;">
		<question-box value="2"><p>質問1です</p></question-box>
		<question-box value="0"><p>質問2です</p></question-box>
	</div>

	<script>
	var qs = document.getElementsByTagName("question-box");
	for(var i=0; i<qs.length; i++){
		console.log(qs[i].value);
		qs[i].value += 1;
	}
	</script>

</body>
</html>

説明

QuestionBoxクラス

このカスタムタグを作るためのクラスです。
template()、constructor()、connectedCallback()などは前のサンプルと同じことなので説明しません。

属性valueのgetter/setterとシャドウルート以下の要素との連動の説明が主眼です。

概要

このカスタムタグはラジオボタンをクリックすると選択されたラジオボタンの値がカスタムタグ自体のvalue属性の値になると言うものです。

getter

「get value()」がgetterです。
これはこのカスタムタグのvalueプロパティを参照した時に取得できる値として機能します。
実際にはカスタムタグ内の<input>のうちcheckedになっているもののvalueを取得します。

this.shadowRoot.querySelectorAll("input")でシャドウルート以下の<input>を取得します。
なお、シャドウルート以下の要素の対してはelement.getElementsByTagName()やelement.getElementsByClassName()は機能しないのでquerySelectorAll()を使用します。
querySelector()とgetElementById()は機能します。

あとは普通にcheckedの<input>を探し、そのvalueを返すだけです。

setter

「set value()」がgetterです。
querySelectorAll()を使っているのはgetterで説明した通りです。
あとは普通にsetterの引数valと一致するvalueを持つ<input>をcheckedにするだけです。

これだけでJSで操作するときのプロパティはもちろん、HTMLから値を設定するときのvalue属性も有効になります。
下がサンプルで、1つめはvalue=2、2つめはvalue=0を属性として指定しています。

質問1です

質問2です

動作確認

87〜93行目のコードは動作確認です。
console.log()で値が取得できることを確認し、そのあと+1してプロパティの変更が有効なことを確認しています。
実際には次の様になります。

質問1です

質問2です

値の取得はコンソールをみないとダメですが、選択しているラジオボタンが+1されているのが確認できます。

まとめ

カスタムタグ自体の属性やプロパティはgetter/setterを実装することで、カスタムタグ内の動作と連動させることができます。
これは単純なサンプルだからと言うのもありますが、getter/setterを実装するだけと言うシンプルな方法で実現できるのは素晴らしいです。