webcomponentsやesmのjsのscope

jsの読み込み方と、定義が見えるかどうか。変数のスコープ

MACのChrome バージョン: 76.0.3809.132


| | head内 | body直下 | body内の子要素 | template内 | templateからコピーされたbody直下要素 | shadow-dom内 | 
| --- | --- | --- | --- | --- | --- | --- |
| class定義 | 読めた | 読めた | 読めた | 読めなかった | 読めた | |
| global変数定義 | 読めた | 読めた | 読めた | 読めなかった | 読めた | 読めた |
| global変数定義(esm) |  読めなかった | 読めなかった | 読めなかった | | | |
| window変数定義(esm) | 読めた | | | 読めなかった | 読めた | |
| export(esm) | 読めなかった | 読めなかった | | | 読めなかった | |

このことから

- shadow-domは特にjsの実行環境を隔離したりはしない
- esmを使ってもesmの中でwindow変数を弄られたら無理
- template(DocumentFragment)は、実際にFragmentじゃないDomに追加するまで、読めないしそもそも定義が走ってないっぽい

という事がわかる。

つまり、webcomponentを作るときに、副作用なく&自己管理なものを作るとなると、

- html/cssについては、shadow-domを使えば思考停止でOK
- jsは、exportが定義されている行儀のよいesmのみを利用する
- jsは、windowに書き込まない行儀のよいもののみ利用する
- jsは、idはclassを扱わず、直接elementを扱う行儀のよいもののみ利用する

を守る必要がある。現実にそんなお行儀の良いjsなわけはないので、現実的ではない。

「副作用なく&自己管理」を諦めて、

- 「webcomponentの依存ライブラリを、予めmainHTML側の責任で読み込む」という使い方を強いる
- jsは、idはclassを扱わず、直接elementを扱う行儀のよいもののみ利用する
- 「webcomponent側で、引数にshadowRootを取りjs関係をactivateする関数を提供し、mainHTML側の責任でそれをcallさせる」という使い方を強いる

が限界か。

---

## 定義箇所詳細

### head内

```html:head内
<head>
  <script></script><!-- ← ここで定義 -->
</head>
<body>
  <button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
```

### body直下

```html:body直下
<body>
  <script></script> <!-- ← ここで定義 -->
  <button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
```

### body内の子要素

```html:body内の子要素
<body>
  <main>
      <script></script> <!-- ← ここで定義 -->
  </main>
  <button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
```

### template内

```html:template内
<head>
  <template>
    <script></script> <!-- ← ここで定義 -->
  </template>
</head>
<body>
  <button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
```

### templateからコピーされたbody直下要素

```html:templateからコピーされたbody直下要素
<head>
  <template>
    <script></script> <!-- ← ここで定義 -->
  </template>
</head>
<body>
  <script>
      const body = document.querySelector('body');
      const template = document.querySelector('template');
      body.appendChild(
        document.importNode(template.content, true)
      );
  </script>
  <button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
```

### shadow-dom内

```html
<head>
  <template>
    <script></script> <!-- ← ここで定義 -->
  </template>
</head>
<body>
  <main></main>
  <script>
    const main = document.querySelector('main');
    const template = document.querySelector('template');
    main.attachShadow({mode: 'open'}).appendChild(document.importNode(template.content, true));
  </script>
  <button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
```

## 定義方法詳細

### class定義したinlinejs

```html
<script src="./XxxClass.js"></script>
```

```javascript:./XxxClass.js
class XxxClass {
}
```

### global変数定義

```html
<script src="./XxxGlobalVar.js"></script>
```

```javascript:./XxxGlobalVar.js
xxx = "{{{xxx}}}";
```

### global変数定義(esm)

```html
<script type="module" src="./XxxGlobalVarEsm.js"></script>
```

```javascript:./XxxGlobalVarEsm.js
xxx = "{{{xxx}}}";
```

### window変数定義(esm)

```html
<script type="module" src="./XxxWindowVarEsm.js"></script>
```

```javascript:./XxxGlobalVarEsm.js
window.xxx = "{{{xxx}}}";
```

### export(esm)

```html
<script type="module">
  import { xxx } from "./XxxExport.js";
</script>
```

```javascript:XxxExport.js
export const xxx = '{{{xxx}}}';
```