最近の要素・ノード操作型メソッド事情

# はじめに

JavaScript もどんどん進化し、ライブラリ等を使わずにネイティブな記述だけでもいろんな処理が書けるようになってきています。
実際に使うかどうかはおいておき、今こんなのがあるよというやつです。

※以下、jQuery を使わない書き方を「ネイティブ」と表現しています

# jQuery っぽいメソッド

もう最近では jQuery を使わないことの方が多い気がします。
なんか気づいたら jQuery で使っていたメソッドのようなものがネイティブで書けるようになってます。

## `.closest()`

`.closest()` といえば、直近の祖先の要素を取得するやつです。
ネイティブでは `Element.closest()` として実装されています。

```html:HTML
<div class="target">
  <div class="target">
    <div>
      <div id="grandchild"></div>
    </div>
  </div>
</div>
```

```js:jQuery
var target = $('#grandchild').closest('.target');
target.prop({ id: 'grandparent' });
```

```js:JavaScript
const target = document.getElementById('grandchild').closest('.target');
target.id = 'grandparent';
```

これは両方とも以下になります。

```html:HTML
<div class="target">
  <div class="target" id="grandparent">
    <div>
      <div id="grandchild"></div>
    </div>
  </div>
</div>
```

## `.prepend()` と `.append()`

`.prepend()` と `.append()` といえば、前者が指定した要素の最初に要素を挿入、後者が指定した要素の最後に要素を挿入するやつです。
ネイティブでそれぞれ `ParentNode.prepend()` と `ParentNode.append()` というメソッドとして実装されています。

`Node.appendChild()` と `ParentNode.append()` の違いは返り値です。
返り値については、前者は挿入される要素・ノードなのに対して、後者は `undefined` です。

```html:HTML
<div id="target">
  <p>ABCDEFG</p>
</div>
```

```js:jQuery
const target = $('#target');

// .prepend()
const span = $('<span>');
target.prepend(span);

// .append()
const div = $('<div>');
target.append(div);
```

```js:JavaScript
const target = document.getElementById('target');

// .prepend()
const span = document.createElement('span');
target.prepend(span);

// .append()
const div = document.createElement('div');
target.append(div);
```

これは両方とも以下になります。

```html:HTML
<div id="target">
  <span></span>
  <p>ABCDEFG</p>
  <div></div>
</div>
```

### 引数で複数指定が可能

`.prepend()` と `.append()` は引数を増やすことで複数の要素・ノードを挿入できます。

```html:HTML
<div id="target">
  <p>ABCDEFG</p>
</div>
```

```js:JavaScript
const target = document.getElementById('target');

const span01 = document.createElement('span');
const div01 = document.createElement('div');

target.prepend(span01, div01, '12345');

const span02 = document.createElement('span');
const div02 = document.createElement('div');

target.append(span02, div02, '67890');
```

結果は

```html:HTML
<div id="target">
  <span></span>
  <div></div>
  12345
  <p>ABCDEFG</p>
  <span></span>
  <div></div>
  67890
</div>
```

ちょっと気をつけたいのが `.prepend()` ですね。引数で複数指定した場合は、引数の順番のまま挿入されます。

### テキストの場合の挙動の違い

一見すると同じように見えますが、テキストの場合は jQuery とネイティブで違いが出てきます。
`.append()` で例を見ます。

```html:HTML
<div id="test">
  <p>ABCDEFG</p>
</div>
```

```js:JavaScript
const list = document.getElementById('test');

// jQuery
$(list).append('<p>jQuery</p>');

// native
list.append('<p>native</p>');
```

結果は……

```html:HTML
<div id="test">
  <p>ABCDEFG</p>
  <p>jQuery</p>
  &lt;p&gt;native&lt;/p&gt;
</div>
```

jQuery ではHTML文字列として扱われ、ネイティブではテキストノードとして扱われます。

ネイティブでHTML文字列として挿入したい場合は `element.insertAdjacentHTML` で代用できます。

```js:JavaScript
const list = document.getElementById('test');
list.insertAdjacentHTML('beforeend', '<p>native</p>');
```

### 仕様

[ParentNode.append() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/ParentNode/append)
[ParentNode.prepend() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/ParentNode/prepend)


## `.before()` と `.after()`

`.before()` と `.after()` といえば、前者が指定した要素の前に要素を挿入、後者が指定した要素の後ろに要素を挿入するやつです。
ネイティブでそれぞれ `ChildNode.before()` と `ChildNode.after()` というメソッドとして実装されています。

```html:HTML
<p id="target">ABCDEFG</p>
```

```js:jQuery
const target = $('#target');

// .before()
const span = $('<span>');
target.before(span);

// .after()
const div = $('<div>');
target.after(div);
```

```js:JavaScript
const target = document.getElementById('target');

// .before()
const span = document.createElement('span');
target.before(span);

// .after()
const div = document.createElement('div');
target.after(div);
```

これは両方とも以下になります。

```html:HTML
<span></span>
<p id="target">ABCDEFG</p>
<div></div>
```

### 引数で複数指定が可能

`.before()` と `.after()` は引数を増やすことで複数の要素・ノードを挿入できます。

```html:HTML
<p id="target">ABCDEFG</p>
```

```js:JavaScript
const target = document.getElementById('target');

const span01 = document.createElement('span');
const div01 = document.createElement('div');

target.before(span01, div01, '12345');

const span02 = document.createElement('span');
const div02 = document.createElement('div');

target.after(span02, div02, '67890');
```

結果は

```html:HTML
<span></span>
<div></div>
12345
<p id="target">ABCDEFG</p>
<span></span>
<div></div>
67890
```

### テキストの場合の挙動の違い

`.prepend()` と `.append()` の時と同じように、テキストの場合は jQuery とネイティブで違いが出てきます。

```html:HTML
<p id="target">ABCDEFG</p>
```

```js:JavaScript
const target = document.getElementById('target');

// jQuery
$(target).after('<p>jQuery</p>');

// native
target.after('<p>native</p>');
```

結果は……

```html:HTML
<p id="target">ABCDEFG</p>
<p>jQuery</p>
&lt;p&gt;native&lt;/p&gt;
```

jQuery ではHTML文字列として扱われ、ネイティブではテキストノードとして扱われます。

ネイティブでHTML文字列として挿入したい場合は `element.insertAdjacentHTML` で代用できます。

```js:JavaScript
const target = document.getElementById('target');
target.insertAdjacentHTML('afterend', '<p>native</p>');
```

### 仕様

[ChildNode.before() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/ChildNode/before)
[ChildNode.after() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/ChildNode/after)


## `.remove()`

`.remove()` といえば、DOMツリーから要素を取り除く（削除ではない）メソッドです。
ネイティブでは `ChildNode.remove()` として実装されています。

`Node.removeChild()` との違いは、指定した要素が `removeChild` を実行する要素内に存在している必要があります。
要するに、取り除く要素の親要素から実行してあげる必要があります。自分自身を取り除くメソッドではありません。
`ChildNode.remove()` は、自分自身を取り除くメソッドです。

```html:HTML
<p id="target">ABCDEFG</p>
<p>HIJKLMN</p>
```

```js:jQuery
$('#target').remove();
```

```js:JavaScript
document.getElementById('target').remove();
```

これは両方とも以下になります。

```html:HTML
<p>HIJKLMN</p>
```

### 仕様

[ChildNode.remove() - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove)


## `.replaceWith()`

`.replaceWith()` は要素を置換するメソッドで、シンプルな使い方では jQuery とネイティブでは動作的にほぼ同じです。
`ChildNode.replaceWith()` は jQuery のように引数に関数を使えません。

```html:HTML
<p id="target">ABCDEFG</p>
```

```js:jQuery
const div = $('<div>');
$('#target').replaceWith(div);
```

```js:JavaScript
const div = document.createElement('div');
document.getElementById('target').replaceWith(div);
```

これは両方とも以下になります。

```html:HTML
<div></div>
```

### テキストの場合の挙動の違い

ここまでくればおわかりかと思いますが、他のメソッド同様に挙動が異なります。

```html:HTML
<p id="target01">ABCDEFG</p>
<p id="target02">HIJKLMN</p>
```

```js:JavaScript
// jQuery
$('#target01').replaceWith('<p>jQuery</p>');

// native
document.getElementById('target02').replaceWith('<p>native</p>');
```

結果は……

```html:HTML
<p>jQuery</p>
&lt;p&gt;native&lt;/p&gt;
```

jQuery ではHTML文字列として扱われ、ネイティブではテキストノードとして扱われます。

ネイティブでHTML文字列として挿入したい場合は `Element.outerHTML` で代用しようと思えばいけます。

```js:JavaScript
const target02 = document.getElementById('target02');
target02.outerHTML = '<p>native</p>';
```

### 仕様

[ChildNode.replaceWith() - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/replaceWith)


# 思ったこと

相変わらずDOM系のメソッドは似たようなものがたくさんあったりしてよくわからんですね……