質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.05%

[JS]DOMContentLoadedイベントを起こしたところ関数が参照できなくなりました。

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 327

norun07

score 8

app/assets/javascripts/main.js内

window.addEventListener('DOMContentLoaded', function(){
  // Option
  var CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxxxxx';
  var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest"];

  var SCOPES = "https://www.googleapis.com/auth/youtube.readonly";

  var authorizeButton = document.getElementById('authorize-button');
  var signoutButton = document.getElementById('signout-button');
  var content = document.getElementById('content');
  var channelForm = document.getElementById('channel-form');
  var channelInput = document.getElementById('channel-input');
  var videoContainer = document.getElementById('video-container');
  var defaultChannel = 'techguyweb';

  // Form submit and change channel
  channelForm.addEventListener('submit', function(e){
    e.preventDefault();

    var channel = channelInput.value;

    getChannel(channel);
  });


  // Load auth2 libraly
  function handleClientLoad(){
    gapi.load('client:auth2' , initClient);
  }

  // Init API client library and set up sign in listeners
  function initClient(){
    gapi.client.init({
      discoveryDocs: DISCOVERY_DOCS,
      clientId: CLIENT_ID,
      scope: SCOPES
    }).then(() => {
      //Listen for sign in state changes
      gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
      // Handle initial sign in state
      updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
      authorizeButton.onclick = handleAuthClick;
      signoutButton.onclick = handleSignoutClick;
    });
  }

  // Update UI sign in state changes
  function updateSigninStatus(isSignedIn) {
    if(isSignedIn){
      authorizeButton.style.display = 'none';
      signoutButton.style.display = 'block';
      content.style.display = 'block';
      videoContainer.style.display = 'block';
      getChannel(defaultChannel);
    } else {
      authorizeButton.style.display = 'block';
      signoutButton.style.display = 'none';
      content.style.display = 'nonek';
      videoContainer.style.display = 'none';
    }
  }

  // handle login
  function handleAuthClick(){
    gapi.auth2.getAuthInstance().signIn();
  }

  // handle logout
  function handleSignoutClick(){
    gapi.auth2.getAuthInstance().signOut();
  }

  // Display channel data
  function showChannelData(data){
    var channelData = document.getElementById('channel-data');
    channelData.innerHTML = data;
  }

  // Add commas to number
  function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  }



  // Get channel from API
  function getChannel(channel){
    gapi.client.youtube.channels
      .list({
        part: 'snippet,contentDetails,statistics',
        forUsername: channel
      })
      .then(response => {
        console.log(response);
        var channel = response.result.items[0];

        var output = `
          <ul class='collection'>
            <li class='collection-item'>Title: ${channel.snippet.title}</li>
            <li class='collection-item'>ID: ${channel.id}</li>
            <li class='collection-item'> Subscribers: ${
              numberWithCommas(channel.statistics.subscriberCount)
            }</li>
            <li class='collection-item'>Views: ${
              numberWithCommas(channel.statistics.viewCount)
            }</li>
            <li class='collection-item'>Videos: ${
              numberWithCommas(channel.statistics.videoCount)
            }</li>
          </ul>
          <p>${channel.snippet.description}</p>
          <hr>
          <a class='btn grey darken-2' target='_blank' href="https://youtube.com/${channel.snippet.customUrl}">
          Visit Channel</a>
        `;
        showChannelData(output);

        var playlistId = channel.contentDetails.relatedPlaylists.uploads;
        requestVideoPlaylist(playlistId);
      })
      .catch(error => alert('No Channel By That Name'));
  }

  function requestVideoPlaylist(playlistId){
    var requestOptions = {
      playlistId: playlistId,
      part: 'snippet',
      maxResults: 10
    }

    var request = gapi.client.youtube.playlistItem.list(requestOptions);

    request.execute(response => {
      console.log(reaponse);
      var playListItems = response.result.items;
      if(playListItems){
      var output = '<h4 class="align-center">Latest Videos</h4>';

      // Loop through videos and append output
      playListItems.forEach(item => {
        var videoId = item.snippet.resourceId.videoId;

        output += `
          <div class="col s3">
          <iframe width="100%" height="auto" src="https://www.youtube.com/embed/
          ${videoId}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
          </div>
        `;
      });

      // Output videos
      videoContainer.innerHTML = output;

      } else {
        videoContainer.innerHTML = 'No Uploaded Videos';
      }
    });
  }
});


大変、記述が助長になってしまい申し訳ございません。
より詳細に分析して頂きたいのでよろしくお願い致します。
Uncaught ReferenceError: handleClientLoad is not defined
at HTMLScriptElement.onload
というエラーが出ており、
function handleClientLoad(){
gapi.load('client:auth2' , initClient);
}
こちらの関数が参照できません。
youtubeのチャンネル名をフォーム内に記述するとその結果がhtmlとして表示されると言うものです。
一応、googleアカウントでログインがされると検索フォームが現れるという仕様になっております。

%section
  .container
    %p Log in With Google
    %button.btn.red#authorize-button
      Log in
    %button.btn.red#signout-button
      Log Out
    %br/
    #content
      .row
        .col.s6
          %form#channel-form
            .input-field.col.s6
              %input#channel-input{:placeholder => "Enter Channel Name", :type => "text"}
              %input.btn.grey.lighten-2#submits__btn{:type => "submit", :value => "Get Channel Data"}
        .col.s6#channel-data
      .row#video-container
%script{:src => "/assets/main.js"}

%script{:async => "https://apis.google.com/js/api.js", :defer => " https://apis.google.com/js/api.js", :onload => "this.onload=function(){};handleClientLoad()", :onreadystatechange => "if (this.readyState === 'complete') this.onload()", :src => "https://apis.google.com/js/api.js"}


元々、DOMContentLoadedイベントをjs全体に記述する前は、channelForm.addEventListener('submit' function(e){})の部分でchannelFormのオブジェクトがnullというエラーが出ておりました。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+3

functionは一式、window.addEventListenerのスコープ外においてください

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/31 17:49

    承知いたしました。

    channelForm.addEventListener('submit', function(e){
    e.preventDefault();

    var channel = channelInput.value;

    getChannel(channel);
    });
    こちらはスコープ内に置いたままで宜しいでしょうか?

    キャンセル

  • 2020/01/31 18:21

    channelInputが同じスコープ内で宣言されているのでなんとか
    なりそうな気はしますけど、なんともいえません。
    addEventListenerのコールバックには外部から変数を
    もっていきづらいですね。
    bindしたりプロパティで渡してe.targetから参照するとか
    やりようはありますが・・・

    キャンセル

  • 2020/01/31 18:34

    いったんRubyのHaml離れて静的HTMLからの操作にしたほうが良さそう。

    キャンセル

checkベストアンサー

+2

こちらの関数が参照できません。

はい、関数内に書いた以上、関数にローカルなものとなりますので、外側に置いた<script>からの参照はできなくなります。

いちばん簡便には、https://apis.google.com/js/api.jsについてはasyncdeferによる遅延を諦めて、application.jsより先にロードを行って、DOMContentLoadedの中で直接gapi.load('client:auth2' , initClient);を実行する方法が考えられます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/31 18:00

    すみません
    application.jsの読み込み行がどこなのかわかりません。_ _

    キャンセル

  • 2020/01/31 18:02

    layoutの中です(以前の質問で上がっていたかと思います)。

    キャンセル

  • 2020/01/31 18:09

    %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %title Tube
    = csrf_meta_tags
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
    %script{:onload => "this.onload=function(){};handleClientLoad()", :onreadystatechange => "if (this.readyState === 'complete') this.onload()", :src => "https://apis.google.com/js/api.js"}
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
    このように記述いたしました。
    ですが、
    VM981 :23 Uncaught ReferenceError: handleClientLoad is not definedこのエラーが出てしまいます。

    キャンセル

+1

とりあえずその関数だけ動けばよいのであれば。

  // Load auth2 libraly
  function handleClientLoad(){
    gapi.load('client:auth2' , initClient);
  }

  // Load auth2 libraly
  window.handleClientLoad = function (){
    gapi.load('client:auth2' , initClient);
  }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/31 17:15

    handleClientLoadをセットするのがDOMContentLoadedの中だと、タイミング的に<script>のonLoadのほうが先に動いてしまう事故が起きないか、ちょっと木になります。

    キャンセル

  • 2020/01/31 17:20

    To: maisumakunさん
    そうですね、キャッシュされることも考えると結構事故がおきそうですね。

    キャンセル

  • 2020/01/31 17:34

    回答ありがとうございます。
    ご指示通りに対応いたしましたが次はchannelForm.addEventListener('submit', function(e)
    こちらのオブジェクトが空と言われました。

    キャンセル

0

document.addEventListener("DOMContentLoaded", function(){
  document.getElementById('channel-form').addEventListener('submit', function(e){
    e.preventDefault();

    var channel = channelInput.value;

    getChannel(channel);
  });
}, false);


このように記述したところコンソールにエラーは表示され無くなりました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る