【JavaScript】Uncaught TypeError: Cannot read property ‘プロパティ名’ of nullのエラーについて

すごく初歩的なエラーで1時間くらい時間を使ってしまったので
備忘録として残したいと思います。


### 前提

学習のためにメルカリのようなアプリを作成中です。
JavaScriptの非同期通信を用いて、販売価格を入力すれば
手数料と、入力した価格から手数料を差し引いた金額が自動で表示される機能を実装しようと下記のようにコードを記述しました。

```application.js

require("@rails/ujs").start()
require("@rails/activestorage").start()
require("channels")
require("../item_price");

```

```items/new.html.erb

 <%# 販売価格 %>
    <div class="sell-price">
      <div class="weight-bold-text question-text">
        <span>販売価格<br>(¥300〜9,999,999)</span>
        <a class="question" href="#">?</a>
      </div>
      <div>
        <div class="price-content">
          <div class="price-text">
            <span>価格</span>
            <span class="indispensable">必須</span>
          </div>
          <span class="sell-yen">¥</span>
          <%= f.text_field :price, class:"price-input", id:"item-price", placeholder:"例）300" %>
        </div>
        <div class="price-content">
          <span>販売手数料 (10%)</span>
          <span>
            <span id='add-tax-price'></span>円
          </span>
        </div>
        <div class="price-content">
          <span>販売利益</span>
          <span>
            <span id='profit'></span>円
        </div>
        </span>
      </div>
    </div>
    <%# /販売価格 %>

```

```layouts/application.html.erb

<!DOCTYPE html>
<html>

<head>
  <title>Furima</title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>
  <script type="text/javascript" src="https://js.pay.jp/v1/"></script>
  <%= stylesheet_link_tag 'application', media: 'all'%>
  <%= javascript_pack_tag 'application' %>
</head>

<body>
  <%= yield %>
</body>

</html>

```

```item_price.js

const priceInput = document.getElementById("item-price");
priceInput.addEventListener("input", () => {
  const inputValue = priceInput.value;
  const addTaxDom = document.getElementById('add-tax-price');
  addTaxDom.innerHTML = Math.floor( parseInt(inputValue) * 0.1 );
  const profitDom = document.getElementById('profit');
  profitDom.innerHTML = Math.floor( parseInt(inputValue) - parseInt(addTaxDom.innerHTML) );
})

```

### 本題

挙動を確認しようと、ブラウザの検証ツールのコンソールで見てみると

```console

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
    at Object../app/javascript/item_price.js (item_price.js:3)
    at __webpack_require__ (bootstrap:19)
    at Object../app/javascript/packs/application.js (application.js:9)
    at __webpack_require__ (bootstrap:19)
    at bootstrap:83
    at bootstrap:83

```

このようなエラーが出ていました。
最初"TypeError"と出ているのでスペルミスかな？と思いましたが、
そうではなく、エラー文しっかり調べてみると
「nullのプロパティからは値を引っ張ってこれないよ」と言われているようです。

ということはビューの記述がおかしいのかと思って調べていると、ある記事に似たような内容で悩んでいる人の記事に答えが載っていました。

HTMLは上から順に解釈されていく仕様になっており、
`<head>`に記述されている"JavaScriptを読み込む"と言う記述が
`<body>`に記述されている内容にたどり着く前に読み込まれてしまうため、
JavaScriptの処理は`<body>`に入力してある値を取得する前に実行されてしまいます。
そのためnullとなり、値がないよ〜と言われてしまうわけです。

### 解決方法

ではどうすれば良いのか。
方法はいくつかあるようですが、今回は下記の記述をJSファイルに追記しました。

```item_price.js

window.addEventListener('load', function(){

  const priceInput = document.getElementById("item-price");
  priceInput.addEventListener("input", () => {
    const inputValue = priceInput.value;
    const addTaxDom = document.getElementById('add-tax-price');
    addTaxDom.innerHTML = Math.floor( parseInt(inputValue) * 0.1 );
    const profitDom = document.getElementById('profit');
    profitDom.innerHTML = Math.floor( parseInt(inputValue) - parseInt(addTaxDom.innerHTML) );
  })
})

```

`window.addEventListener('load', function(){})`
を記述することで、最初に情報を全て取得したから以降の処理を行うことができるので、
nullになることなく、意図した挙動を行うことができました。

しっかり勉強したつもりでしたが、基本的なことをすっかり忘れてしまっていたがために起きたエラーでした。
もう一度復習しなければ。


### 終わり