2002421taiga
@2002421taiga (max 410)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

webpackでJSが読み込まれない

解決したいこと

railsでjavascriptが適応できない(webpackを使っています)
application.html.erbにhome/top.jsを読み込みたいです。
home/top.jsはwebpackで読み込むためにapp/javascriptの配下に置いています。

エラー内容(console)

top.js:14 Uncaught TypeError: Cannot read properties of null (reading 'querySelector')
    at PhotoViewer.updatePhotoViewer (top.js:14:1)
    at PhotoViewer.init (top.js:9:1)
    at Object../app/javascript/home/top.js (top.js:59:1)
    at __webpack_require__ (bootstrap:19:1)
    at Module../app/javascript/packs/application.js (application.js:10:1)
    at __webpack_require__ (bootstrap:19:1)
    at bootstrap:83:1
    at bootstrap:83:1

該当するソースコード

app/views/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Aokako</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <div class='header'>
      <% if user_signed_in? %>
        <ul>
          <li><%= link_to 'インスタ' ,kakomons_path %></li>
          <li></li>
        </ul>
      <% else %>
        <ul>
          <li><%= link_to 'インスタ' ,root_path,class:'aokako-font' %></li>
          <li class='top'><%= link_to 'ログイン' ,new_user_session_path,class:'login-font' %></li>
          <li><%= link_to '新規登録',new_user_registration_path,class:'signin-font' %></li>
        </ul>
      <% end %>
    </div>

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

app/javascript/packs/application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start();
require("turbolinks").start();
require("@rails/activestorage").start();
require("channels");
require("home/top");

import "jquery";
import "popper.js";
import "bootstrap";
import "../stylesheets/application";
import "../stylesheets/homes";


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
app/javascript/home/top.js
class PhotoViewer {
    constructor(rootElm,images){
        this.rootElm = rootElm;
        this.images = images;
        this.currentIndex = 0;
    }

    init(){
        this.updatePhotoViewer();
    }

    updatePhotoViewer() {
        const frameElm = this.rootElm.querySelector('.frame');
        const image = this.images[this.currentIndex];
        frameElm.innerHTML = `
            <div class="image">
                <img src="${image}">
            </div>
        `;

        this.startTimer();
    }

    next(){
        const lastIndex = this.images.length - 1;
        if(this.currentIndex === lastIndex) {
            this.currentIndex = 0
        } else {
            this.currentIndex++;
        }
        this.updatePhotoViewer();
    }

    startTimer() {
        if(this.timerKey) {
            clearTimeout(this.timerKey)
        }

        this.timerKey = setTimeout(()=>{
            this.next();
        },6000);
    }

}

/*global images*/
// eslint-disable-next-line no-native-reassign
images = [
 'assets/images/img.jpeg',
];
new PhotoViewer(document.getElementById('top'),images).init();

自分で試したこと(2つあります)

①app/javascript/にhome/top.jsを作り、
app/javascript/packs/application.jsでrequire('home/top’)とやって、rails serverを起動し直した。
②app/javascript/packにhome/application.jsを作り、そこで
require('home/top')と書いてapp/javascript/home/top.jsを読み込む。そしてapp/views/home/top.html.erbで
<% javascript_pack_tag 'home/application' %>とかいて読み込んだ。
→後々turbolinksを使っているため、②のやり方はできないということがわかったのでソースコードは①のやり方でやっています。

vscodeでhtmlとjsだけで確認したときはちゃんと作動したのでjsの書き方などに問題があるわけではないかと思います。top.jsでconsole.logで確かめたところエラーのようにconsoleに表示されなかったため、top.jsが読み込めていないんだと思います。ご協力お願いします。

0

1Answer

top.jsは読み込めていますので、まずはきちんとエラーメッセージを順に追っていくことをお勧めします。

top.js:14 Uncaught TypeError: Cannot read properties of null (reading 'querySelector')
    at PhotoViewer.updatePhotoViewer (top.js:14:1)
    at PhotoViewer.init (top.js:9:1)
    at Object../app/javascript/home/top.js (top.js:59:1)

これは「あるオブジェクトがnullで、そのプロパティであるquerySelectorを読めません」ということです。
エラーの通り、PhotoViewer.updatePhotoViewer (top.js:14:1) の個所のthis.rootElmがnullであるということです。

また、きちんと順を追ってよんでいくと、top.jsで
new PhotoViewer(document.getElementById('top'),images).init();
でエラーがでたということですので、this.rootElmに代入されるはずのdocument.getElementById('top')がnullということです。
ソースを見る限りid='top'の要素はありませんので、取得できていないのではないでしょうか。

1Like

Comments

  1. @2002421taiga

    Questioner

    ご回答ありがとうございます!
    申し訳ございません。
    top.html.erbのソースコードを記載し忘れておりました。
    <div id="top">
    <div class="frame">
    </div>
    </div>
    こちらの4行です。
    もう一度教えていただけないでしょうか?
  2. その場合、「要素はきちんとあるが取得できていない」原因を考えます。
    一番典型的なものは DOM が構築される前に要素を取得しようとして失敗するパターンです。
    この場合、 DOMが構築された後に取得するということを意識すればよいので、

    document.addEventListener('DOMContentLoaded', () => {
    new PhotoViewer(document.getElementById('top'),images).init();
    });

    のようにするか、コンテンツを読み込んだあとに処理したい部分をinit(関数名は例)にまとめて

    <body onload="init()">
    <!-- 省略 -->
    </body>

    とするか、下記のようにyieldより下でjsを読み込むかでしょうか。

    <body>
    <!-- 省略 -->
    <%= yield %>
    <!-- ここでjsを読み込む -->
    </body>
  3. @2002421taiga

    Questioner

    yieldの下でjsを読み込んだらできました!
    ご回答ありがとうございました!
    助かりました!

Your answer might help someone💌