なでしこで自身のDOM要素を削除したい

【2024年10月7日追記】
先日のアップデートで、本記事と似たようなことを行うための命令が[公式にサポートされました](https://github.com/kujirahand/nadesiko3/commit/292ea25ef3b04b13bf3695cb3861eab1deafa8f4)。クジラ飛行机さん、ありがとうございました。詳しくは、こちらのマニュアルを参考にしてください。

https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E9%83%A8%E5%93%81%E5%89%8A%E9%99%A4

以下は、記事執筆時点での情報です。


# はじめに
最近、こんなものを、日本語プログラミング言語「なでしこ」で作りたいと思った。
![2024-08-31 12-15-55 (online-video-cutter.com).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197285/5ff9474d-4c53-14cb-4fe1-d4b55d52fc9d.gif)

追加ボタンは非常に簡単で、`ラベル作成`という命令を用いることで、比較的簡単に実現できる。

https://nadesi.com/v3/doc/index.php?plugin_browser%2F%E3%83%A9%E3%83%99%E3%83%AB%E4%BD%9C%E6%88%90&show

例えば、以下のようなプログラムを作成すると追加することができる。

```html:index.html
<html>
    <body>
        <div id="result" style="text-align: center"></div>
        <div id="result2" style="text-align: center"></div>

        <script type="なでしこ">
        
            //ここになでしこのプログラムを書く
        
        </script>
        <script type="text/javascript" src="https://nadesi.com/v3/cdn.php?v=3.6.16&f=release/wnako3.js&run"></script>
    </body>
</html>
```

```:なでしこのプログラム
《カウント》に1を代入。
《要素リスト》は空配列。
「result」のDOM要素ID取得して、DOM親要素設定。
「追加」のボタン作成して、《追加ボタン》に代入。
「削除」のボタン作成して、《削除ボタン》に代入。
改行作成。

《追加ボタン》をクリックした時には
	「{《カウント》}番のラベルです」のラベル作成。
	それを《要素リスト》に配列追加。
	《カウント》=《カウント》+1
	改行作成。
ここまで
```

では、削除ボタンはどうすれば良いだろうか。

# なでしこでDOM要素削除処理

調べてみると`DOM子要素削除`という命令があると分かった。この命令は、親要素と子要素を指定し、子要素を削除するというものである。

https://nadesi.com/v3/doc/index.php?plugin_browser%2FDOM%E5%AD%90%E8%A6%81%E7%B4%A0%E5%89%8A%E9%99%A4&show

これを使えば、出来るんじゃないかと考え、以下のようなプログラムを追加してみた。

```:なでしこのプログラム（追加1）
《削除ボタン》をクリックした時には
	もし、(《要素リスト》の要素数)が1以上なら
		《要素リスト》の0から1を配列取り出す。
		それ[0]をDOM要素削除処理。
	ここまで
ここまで


●(《要素》を)DOM要素削除処理とは、
	DOM親要素から《要素》をDOM子要素削除
ここまで
```

このプログラムは一見、正常に動くが、場合によっては正常に動作しない。つまり、関数`DOM要素削除処理`内で参照している`DOM親要素`が途中で書き換わった場合には正常に動作しないのだ。このことは、これまでのなでしこのプログラムの最後に以下のようなプログラムを付け加えることで確認できる。

```:なでしこのプログラム（追加2）
15秒後には
	「result2」のDOM要素ID取得して、DOM親要素設定。
	「ーーーここからresult2ーーー」のラベル作成。
	改行作成。
ここまで
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197285/f7853d1f-dba3-eb0d-1fdb-6576329497ff.png)

`DOM親要素`と`DOM子要素削除`を使っていると、`DOM親要素`の書き換わる可能性があるプログラム内ではDOM要素を削除することが出来ない。

:::note warn
もちろん、自前でどのDOMがどのDOM親要素の子要素に当たるか、管理することで、適切に削除するプログラムを作成することも出来る。（面倒だけど）
:::

# JS実行を使って実現できないか

JavaScriptには自分自身の要素を削除するメソッドがある。こいつを呼び出すことが出来れば万事解決できそう。

https://developer.mozilla.org/ja/docs/Web/API/Element/remove

ということで、なでしこの`JS実行`の出番である。

https://nadesi.com/v3/doc/index.php?plugin_system%2FJS%E5%AE%9F%E8%A1%8C&show

この`JS実行`の命令を用いて、以下のようなプログラムを試してみた。

```diff_html:なでしこのプログラム
●(《要素》を)DOM要素削除処理とは、
-	DOM親要素から《要素》をDOM子要素削除
+	「sys.__findVar("《要素》").remove()」をJS実行
ここまで
```

ただ、このプログラムは上手く動作しない。下記のページに書かれているように関数の引数として渡された削除対象の変数《要素》は、あくまでも関数内からのみ参照できる変数であり、グローバルスコープからは参照できないからである。

https://github.com/kujirahand/nadesiko3/issues/1733

:::note warn
【2024年9月16日追記】
初稿作成時点（8月31日）では記事の通りでしたが、仕様変更があった（検討中？）ようです。Github上の動向に今後も注意してください。
:::

# とりあえず解決策

大人しく、プラグインを作る。DOM要素を削除するためだけに、プラグインをつくるのはどうかしてるかもしれないが、致し方無い。
以下のプログラム全文をコピーして、適当なhtmlファイルに貼り付けてやると最初のGIF画像のように上手く動く。また、後半部分に書かれているプラグイン本体を読み込むと、他のなでしこプログラム上でも`DOM要素削除`命令が実行できるようになる。配列に対しても操作可能なので、例えば`DOM要素全取得`や`タグ一覧取得`と組み合わせても使用することが出来る。

```html:index.html
<html><body>
<script type="text/javascript" src="https://nadesi.com/v3/cdn.php?v=3.6.16&f=release/wnako3.js"></script>
<div id="result" style="text-align: center"></div>
<div id="result2" style="text-align: center"></div>


<script type="なでしこ">

《カウント》に1を代入。
《要素リスト》は空配列。
「result」のDOM要素ID取得して、DOM親要素設定。
「追加」のボタン作成して、《追加ボタン》に代入。
「削除」のボタン作成して、《削除ボタン》に代入。
改行作成。

《追加ボタン》をクリックした時には
	「{《カウント》}番のラベルです」のラベル作成。
	それを《要素リスト》に配列追加。
	《カウント》=《カウント》+1
	改行作成。
ここまで

《削除ボタン》をクリックした時には
	もし、(《要素リスト》の要素数)が1以上なら
		《要素リスト》の0から1を配列取り出す。
		それ[0]をDOM要素削除。
	ここまで
ここまで

15秒後には
	「result2」のDOM要素ID取得して、DOM親要素設定。
	「ーーーここからresult2ーーー」のラベル作成。
	改行作成。
ここまで


</script>
<script type="text/javascript">
    //↓ここからプラグイン本体
	const plugin_delete_dom={
		"meta": {
			type: "const",
			value: {
				pluginName: "delete_dom",
				description: "DOMを削除するためのプラグイン",
				pluginVersion: "3.6.0",
				nakoRuntime: ["wnako"],
				nakoVersion: "3.6.0"
			}
		},
		"DOM要素削除": { // @DOMを削除する // @DOMようそさくじょ
			type: "func",
			josi: [["を","の"]],
			fn: function (el, sys) {
                //elとして、要素配列か要素が与えられると期待している
				if(Array.isArray(el)){
					el.forEach(e=>e.remove());
				}else{
					el.remove();
				}
			}
		},
	}
	if(typeof(navigator)==="object"){
		navigator.nako3.addPluginObject("plugin_delete_dom", plugin_delete_dom)
	}else{
		module.exports=pluginObj
	}
    //↑ここまでプラグイン本体＆プラグイン登録処理
 
	navigator.nako3.runNakoScript();

</script>
</body></html>
```

参考ページ↓

https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2FJS%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9

# おわりに

`element.remove()`を呼び出すだけの`DOM要素削除`は、公式でサポートしても良いんじゃないかと思う。

　

以上。