JQueryをVanilla JSに緩やかに置き換える

元々JQueryを多用していた物をVanilla JS（普通のJavascript)に徐々に書き換えていくポイントメモ的な物と関連記事を集めてきたもの。

***書き換えの前提***

* 時間がある時に、ちょっとずつ変えていく。一気に変更しない。
* 普通のJavascriptであれば５年以上は持ちそう。XMLHttpRequestなんて２０年前からある。
* JQueryを無理に無くそうとはしない。JQueryじゃなくても良いところを書き換えていく。(ただしJQuery1.xは脆弱性があるので3.xにする）
* 中途半端に変えるとわかりにくくなる場合もある。時間と人に余裕があるなら最初から作り直すことも考える。
* そのままでIE11でも動くようにする。Babelの使用までは考えない。
* なるべく自力で書き換える場合の例なので、実際はある程度共通化したり、ライブラリ等を活用した方が良いかもしれない。


### ブラウザによって未対応な物を押さえておく。

Chromeであれば、ES6以降の多くをサポートしているが、特にIEは対応できていないものが多いので把握しておく必要がある。
内容によってはPolyfillを使うことで対応できることもあるが、それによってJSのサイズが肥大化してしまう可能性もある。

* [ECMAScript 6 compatibility table](https://kangax.github.io/compat-table/es6/)
* [[Javascript] IE11非対応らしきモノをかき集める](https://qiita.com/siruku6/items/3465fd6e0588ee35cc78)
* [JavaScriptのブラウザごとの互換性をポリフィル・Babelで解決する！](https://www.sejuku.net/blog/100946)

なお、``let``は、IE11の場合は使用出来るが、SafariはiOS10以上しか使えないため、iPhone4Sや、iOSをアップデートしていない端末があった場合は、動作しない。


### JQueryオブジェクトをJavascriptのDOMに変換

```js
var elm1 = $('.test');
const dom1 = elm1[0]; // インデックス０番目からDOMが取得できる
const dom2 = elm1.get(0); // インデックス０番目からDOMが取得できる
```

### ページ読み込み時のイベント処理

```js
// JQueryの場合
$(function(){
  // DOM利用可能になった場合の処理
});

// Javascriptの場合
document.addEventListener("DOMContentLoaded", function(){
  // DOM利用可能になった場合の処理
});

// JQueryの場合
$(window).on("load", function(){
  // コンテンツが読み込み完了後の処理
});

// Javascriptの場合
window.addEventListener("load", function(){
  // コンテンツが読み込み完了後の処理
});
```
### クリックされたときのイベント処理

```js

// JQueryの場合（対象は一つのみ）
$('.btn').on('click',function(){
   // 処理
});

// Javascriptの場合(対象は一つのみ）
const btn = document.querySelector(".btn");
btn.addEventListener("click", function(){
  // 処理
});

// JQueryの場合（対象は複数ある）
$('.btn').on('click',function(){
   // 処理
});

// Javascriptの場合(対象は複数ある） for文の場合
const btns = document.querySelectorAll(".btn");
for (let i = 0; i < btns.length; i++) {
  btns[i].addEventListener("click",function(){
    // 処理
  });
}

// Javascriptの場合(対象は複数ある） forEachの場合
const nodelist = document.querySelectorAll(".btn");
const btns = Array.prototype.slice.call(nodelist,0); 
btns.forEach(function(btn){
  btn.addEventListener("click",function(){
    // 処理
  });
});
```

* [IEで NodeList を forEach するとエラーになる問題の対処方](https://qiita.com/snjssk/items/8d179566b023703c0663)

### 要素の取得、セレクターの指定

```js
// JQuery
$("セレクター");
```

JQuery独自拡張があるので、セレクタをそのまま置き換えられない場合もある。

参考サイトが詳しい

* [jQueryからネイティブJavaScriptへ置き換えの第一歩](https://qiita.com/nayuneko/items/b3ad3d6bc2687bcdb0f6)
*  [ライブラリを使わない素のJavaScriptでDOM操作](https://qiita.com/kouh/items/dfc14d25ccb4e50afe89)
* [jQuery -> Vanilla JSの変換](https://web-cheatsheet.com/jquery-to-vanillajs)
* [nefe/You-Dont-Need-jQuery](https://github.com/nefe/You-Dont-Need-jQuery)

### 要素の検索、親子兄弟要素へのアクセス、要素の作成・追加、要素の削除、属性の操作、スタイル関連操作

参考サイトが詳しい

* [ライブラリを使わない素のJavaScriptでDOM操作](https://qiita.com/kouh/items/dfc14d25ccb4e50afe89)

### .height() .width() .offset() .scrollTop()

参考サイトが詳しい

* [脱jQuery .height() .width() .offset() .scrollTop()](https://q-az.net/without-jquery-height-width-offset-scrolltop/)


### .find()、.closest()

```js
// .find()
// JQuery
$("#main").find("ul li");

// Javascript
document.getElementById("main").querySelectorAll("ul li");

// .closest()
// JQuery
$('#main').closest('.contents');

// Javascript
// IE以外はOK
document.getElementById("main").closest(".contents");
// IEの場合は参考サイトを参照

```


* [.find() 脱JQuery](https://q-az.net/none-jquery-find/)
* [最も近い先祖要素の取得](http://shanabrian.com/web/javascript/element-closest.php)
* [｢脱jQuery｣ 生JSで.closest()のように直近の親要素を取得する](https://spyweb.media/2018/01/03/jquery-closest-pure-javascript/)
* [closestをjQueryなしで書こうとしたら](https://qiita.com/jkr_2255/items/3fecd90513affa02d515)


### fadeIn、fadeOut、fadeToggle、fadeTo

```js

// JQuery

$('.item').fadeIn(1000); // １秒後にfadeIn
$('.item').fadeOut(1000); // １秒後にfadeOut
$('.item').fadeToggle(1000); // １秒後にfadeToggle

// Javascript
// fadeIn(CSS Transitionを使う)
const ms = 1000;
const elm = document.querySelector(".item");
elm.style.opacity = 0; // 透過度０
elm.style.transition = "opacity " + ms + "ms";  	
setTimeout(function(){elm.style.opacity = 1;}, 1); // 0.001秒後に transition開始(透過度１にする）

// fadeOut(CSS Transitionを使う)
const ms = 1000;
const elm = document.querySelector(".item");
elm.style.opacity = 1; // 透過度1
elm.style.transition = "opacity " + ms + "ms";   	
setTimeout(function(){elm.style.opacity = 0;}, 1); // 0.001秒後に transition開始（透過度0にする）
setTimeout(function(){elm.style.display = "none";}, ms + 10); // 1.01秒後に完全に消す。

// fadeToggle
const ms = 1000;
const elm = document.querySelector(".item");
elm.style.transition = "opacity " + ms + "ms";
if (elm.style.display !== "none") {
	elm.style.opacity = 1;
	setTimeout(function(){elm.style.opacity = 0;}, 1);
	setTimeout(function(){elm.style.display = "none";}, ms + 10);
} else {
	elm.style.opacity = 0;
	elm.style.display = "";
	setTimeout(function(){elm.style.opacity = 1;}, 1);
}
// fadeTo

```

### slideUp、slideDown、slideToggle


```js

// JQuery

$('.item').slideUp(1000); // １秒後にslideUp
$('.item').slideDown(1000); // １秒後にslideDown
$('.item').slideToggle(1000); // １秒後にslideToggle

// Javascript

// slideUp
const ms = 1000;
const elm = document.querySelector(".item");
elm.style.transition = "height " + ms + "ms";
elm.style.height = "";
elm.style.overflow = "hidden";
setTimeout(function(){elm.style.height = "0px";}, 1);

// slideDown
const ms = 1000;
const elm = document.querySelector(".item");
elm.style.transition = "height " + ms + "ms";
elm.style.height = "0px";
elm.style.display = "";
setTimeout(function(){elm.style.height = "";}, 1);

// slideToggle
const ms = 1000;
const elm = document.querySelector(".item");
elm.style.transition = "height " + ms + "ms";
if (elm.style.display !== "none") {
	elm.style.height = "";
	elm.style.overflow = "hidden";
	setTimeout(function(){elm.style.height = "0px";}, 1);
	setTimeout(function(){elm.style.display = "none";}, ms + 10);
} else {
	elm.style.height = "0px";
	elm.style.display = "";
	setTimeout(function(){elm.style.height = "";}, 1);
    setTimeout(function(){elm.style.overflow = "";}, ms + 10);
}


```


### animate

時間によって設置をを徐々に変えるのはCSS Transitionで実装できるがJQueryのanimateと異なって設定できる値は限定されている。詳細は参考サイトを参照。

* [【CSS3】Transition（変化）関連のまとめ](https://qiita.com/7968/items/812d6a21fc4dd9ae9c75)
* [CSS3でアニメーション！transitionとanimationまとめ（サンプル付き）](https://www.flapism.jp/html/416/)


scrollTopなどはCSS Transitionを使えないのでscrollIntoViewを使う。

```js

// 指定箇所まで自動スクロール
// JQuery
$("html,body").animate({scrollTop:$('.foo').offset().top});

// Javascript
document.querySelector('.foo').scrollIntoView(true);
// ChromeとFirefoxであればsmoothscrollも出来る。
document.querySelector('.foo').scrollIntoView({behavior: 'smooth'});

```

* [あまり知られてなさそうなメソッド element.scrollIntoView()](https://qiita.com/amamamaou/items/728d571d508347b2bc82)

もしくはCSS の scroll-behaviorを使う。

* [scroll-behavior - MDN](https://developer.mozilla.org/ja/docs/Web/CSS/scroll-behavior)
* [scroll-snapと​scroll-behaviorで​実現できることを​解説するよ](https://yumeirodesign.jp/blog/201802/future-scroll.html)


### ajax、get、post、ajaxSetup、load


```js

// JQuery
$.ajax({
        type: "POST",
        url: "http://foo.com/submitform.php",
        data: {
                "key1": "data1",
                "key2": "data2"
        }
})
.then(
    function (data) {
        // 成功
        console.log(data);
    },
    function () {
        // 失敗
});

// Javascript
var formData = new FormData();
formData.append("key1", "data1");
formData.append("key2", "data2");
var xhr = new XMLHttpRequest();
xhr.open('POST', "http://foo.com/submitform.php");
xhr.addEventListener('loadend', function() {
  if (xhr.status === 200) {
    console.log(xhr.response);
  } else {
  };
});
xhr.send(formData);
```

fetch APIでもpolyfillを使えばIEでもOK

```html

<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/3.3.1/es6-promise.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.4/fetch.min.js"></script>
<script>
var formData = new FormData();
formData.append("key1", "data1");
formData.append("key2", "data2");
fetch("http://foo.com/submitform.php", {
    method: "post",
    cache: "no-cache",
    headers: {},
    body : formData
}).then(function(response) {
  console.log(response.text());
});
</script>
```

* [一休.comホテルページのスマホ版からjQuery依存を取り除くためにやったこと](https://user-first.ikyu.co.jp/entry/goodbye-jquery)
* [jQueryを使わない書き方 ajax, each, trigger, on/off, extend, deferred, animate, css編](https://mae.chab.in/archives/2821)


### JQuery Lazy Load

画像を遅延で読み込ませるプラグインとしては、JQuery Lazy Loadなどがあるが、

* [Lazy Load(画像の遅延読み込み)を実装する方法](https://syncer.jp/how-to-setting-lazy-load-images)

Chrome限定であればHTMLだけで対応できる。

```html
<img loading="lazy" src="lazy-cat.png" height="100" width="100" />
```

* [Chromeがネイティブlazy-loadをサポート、JSなしで画像を遅延読み込み可能に](https://www.suzukikenichi.com/blog/chrome-76-supports-native-native-loading/)

IE以外であれば、Intersection Observerを使って、表示位置にきたら自動で画像を読み込むように出来る。


```html
<html>
<body>
<img class="lazyload" src="" data-src="sample.jpg" />
<script>
const imgs = document.querySelectorAll('img.lazyload');
const observer = new IntersectionObserver(function(changes){
    changes.forEach(function(change) {
    change.target.src = change.target.dataset.src;
    observer.unobserve(change.target);
    });
});
imgs.forEach(function(img){
    observer.observe(img);
});
</script>
</body>
</html>
```

* [使ってみよう！Intersection Observer！](https://qiita.com/ryo_hisano/items/42f5980720bc832e6e09)
* [Intersection Observer が良さそうなので試してみた](https://qiita.com/yamanoku/items/027308e23cfc69845d7e)

IEの場合は、単に画面ロード時にimgを置き換えるという手も。

```html
<html>
<body>
<img class="lazyload" src="" data-src="sample.jpg" />
<script>
window.addEventListener("load", function(){
  const imgs = document.querySelectorAll('img.lazyload');
  for (let i = 0; i < imgs.length; i++) {
    imgs[i].setAttribute('src', imgs[i].getAttribute('data-src'));
  } 
});
</script>
</body>
</html>
```