【rails入門】javascriptを用いてカーソルを制御する

# はじめに
皆さんこんな経験ありませんか？
---

![スクリーンショット 2022-04-15 11.41.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2586059/eddd0af0-5dbb-3b62-1aca-e1fd8cc94197.png)


ログインするときメールアドレスの次にパスワードを入力しようとしたけど、保存してるパスワードとか予測変換とかでパスワードの入力欄が隠れてしまう！！
---
# 今回やること
javascriptを用いてメールアドレスを入力し終わってenterを押すと自動でパスワード入力欄にカーソルが合わさるようにする

# 早速やっていきましょう！
前提としてrailsでdeviseというGemを使用してログイン機能を実装しているとします！
### 1.現状のコード
```html:views/users/sessions/new.html.erb
<h2>ログイン</h2>
<p>※登録したメールアドレスとパスワードを入力してください。</p>

<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
  <div class="field">
    <h2>メール </h2><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email", id:"email" %>
    <!-- autofocus: true と id:"email" をつけてるよ！！ -->
  </div>

  <div class="field">
    <h2>パスワード </h2><br />
    <%= f.password_field :password, autocomplete: "off", id:"password" %>
    <!-- id:"password" をつけてるよ！！ -->
  </div>

  <% if devise_mapping.rememberable? -%>
    <div class="field">
      <%= f.check_box :remember_me %>
      <h2>私を覚える</h2>
    </div>
  <% end -%>

  <div class="actions">
    <%= f.submit "Log in" %>
  </div>
<% end %>

<%= render "users/shared/links" %>
```
重要なところはemailのフォームに`autofocus: true`がついていることと、
email, passwordのフォームに`id`をつけていることです！

`autofocus: true`はページが表示されて一番最初にカーソルが合う場所に指定しています！
`id`はjavascriptで取得しやすいようにつけてます！

### 2. javascriptを書いていきます！
```javascript:views/users/sessions/new.html.erb

<h2>ログイン</h2>
<p>※登録したメールアドレスとパスワードを入力してください。</p>

<!-- 省略 -->

<%= render "users/shared/links" %>

<script>
  const email = document.querySelector('#email'); //emailの入力フォーム取得
  const pass = document.querySelector('#password'); //passwordの入力フォーム取得
  email.onkeydown = function(event){ //キーボード押されたら
    if (event.key === 'Enter') { //そのキーボードがenterキーなら
      pass.focus(); //passwordの入力フォームにフォーカスする
      return false; //これがなかったら一瞬しかカーソル合わない
    }else{
      return true;
    }
  }
</script>
```
以上！！

### 発展編①
ここからは発展的な内容でやっていきます！！発展とはいえちょっとしたお遊びなので最後まで見ていって下さい

まずはpassword入力欄でenterを押すと勝手にloginボタンを押してくれるようにします！
```javascript:views/users/sessions/new.html.erb

  <!-- 省略 -->

  <div class="actions">
    <%= f.submit "Log in", id:"login" %>　<!-- idを追加 -->
  </div>

  <!-- 省略 -->

<script>

  // 省略

  // ここから追記
  pass.onkeydown = function(event){
    if (event.key === 'Enter') {
      document.querySelector("#login").click();
    }
  }
  //　ここまで
</script>
```

### 発展編②
フォームがもっとたくさんあるときenter押したら一つ下のフォームにカーソルが合うようにする
htmlファイルはこんな感じです
```html:form.html.erb
<%= form_for @post do |f| %>
    <div class="all">
        <div class="element">
            <%= f.text_field :body1, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body2, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body3, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body4, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body5, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body6, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body7, class:"form" %>
        </div>

        <%= f.submit "post", id:"post" %>
    </div>
<% end %>
```
javascriptはこんな感じ
```javascript:form.html.erb
<%= form_for @post do |f| %>

<!-- 省略　-->

<% end %>

<script>
    const forms = document.querySelectorAll('.form'); //class名がformの要素をlistとして取得
    function nextfocus(e){
        if (e.keyCode === 13){　//キーボードがenterキーの時
            this.parentNode.nextElementSibling.children[0].focus();
　　　　　　　　　　　　　　　　　　　　　　　　//めちゃややこしいですが、要素を辿っています
        }
    }
    for (let i = 0;i < forms.length - 1;i++){
        forms[i].addEventListener("keypress", nextfocus);
　　　　　　　　　　　　　　　　//最後のform以外の全てのformに対してキーボードが押されたときのfunctionを設定する
    }
</script>
```
解説
```
this.parentNode.nextElementSibling.children[0].focus();
```
ここでは要素を辿っています！
* parentNode → 親要素取得
* nextElementSibling　→ 同じ親要素を持つ次の要素取得
* children[0]　→ 子要素の中の1つ目の要素

今やりたいことはenterが押されたときに一つ下のフォームへカーソルを動かすことです:frowning2:
ですが、ただ一つ下の要素というわけではなく、一つ一つの`form`は`div`で囲まれています:frowning2::frowning2:
なので、カーソルの移動先はenterが押されたformの親要素のdiv(class="element")の次の要素のなかのformということになります！
以下の画像参照↓↓↓
![スクリーンショット 2022-04-16 2.21.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2586059/c4ce3db3-3164-282a-f2be-f023bd043b12.png)

#### ここでちょっと問題発生:rotating_light::rotating_light:
enterを押すとsubmitボタンが勝手に押されてしまう

なのでちょっと変更
```html:form.html.erb
<!-- 変更前 -->
<%= f.submit "post", id:"post" %>

<!-- 変更後 -->
<button type="button" onclick="submit()" id="post">post</button>
```
`type="submit"`が良くなかったようです

### 発展編②続き
さらに発展編②に付け加えていきましょう！！
まずは発展編①でもやりましたが、最後のフォームでenterを押すとsubmitされるようにしましょう

```javascript:form.html.erb
<script>

    //追記
    forms[forms.length-1].onkeydown = function(event){
        if (event.key === 'Enter') {
            document.querySelector('#post').click();
        }
    }

</script>
```
以上！
# 最後に
今回のコードこんな感じ
```html:form.html.erb
<%= form_for @post do |f| %>
    <div class="all">
        <div class="element">
            <%= f.text_field :body1, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body2, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body3, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body4, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body5, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body6, class:"form" %>
        </div>
        <div class="element">
            <%= f.text_field :body7, class:"form" %>
        </div>

        <button type="button" onclick="submit()" id="post">post</button>
    </div>
<% end %>

<script>
    const forms = document.querySelectorAll('.form');
    function nextfocus(e){
        if (e.keyCode === 13){
            this.parentNode.nextElementSibling.children[0].focus();
        }
    }
    for (let i = 0; i < forms.length - 1; i++){
        forms[i].addEventListener("keypress", nextfocus);
    }
    forms[forms.length-1].onkeydown = function(event){
        if (event.key === 'Enter') {
            document.querySelector('#post').click();
        }
    }
</script>
```
ここまで見ていただきありがとうございます！
僕はこれからもたくさん記事を書いてアウトプットしていこうと思っています！間違いや不明点、改善点などなんでもコメントしていただきたいです！
それではばいばいばあああい！！！！