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

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

ただいまの
回答率

89.10%

Javascript: 引数が空の時クリック処理を実行するとエラーになってしまう

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 734

nanase21

score 91

最初に/お詫び
前回質問した内容とほぼ同じ内容になっています。

前提

クリック時の処理
問題なくクリック処理が実行されると、「エラーは取れましたね」とアラートが表示される。

起きているエラー
schedule.setHoliday()の中が空の時カレンダーをクリックするとエラーになってしまう。

関数の処理について
setHoilday関数では、引数の値を元に定休日の日には表示されているカレンダーのセルが「×」と表示される処理になっています。

エラーの原因
setHoilday()の引数が関数に返された時に値が空の状態だと関数内の処理が実行されないため
setHoilday(1,2,4)と一つでも値が入っていれば、エラーは起きない。

どうしたいのか
定休日を設定したくない時はschedule.setHoliday()を空で返したい。

コード量が多いためcodepenに関連する全コードを載せています。
codepen 全コード

実現したいこと

現状では、定休日を最低1日セットしないとエラーが出てしまうので、それをsetHoilday()と定休日をセットしない時でもクリック時にアラートを表示できるようにしたい。

追記

ご指摘を受けた点
codepenのcssとjsを分けました。
コード量が文字数上、全てお載せすることができないため、該当するコードを追記しました。

該当コード

  setHoliday (...dayNo) {
            let
              year   = this.current.getFullYear (),
              start  = new Date (year, 0, 1),
              end    = new Date (year, 12, 1),

              b = new Date (Date.UTC (1970,0,1, 0, 0)),//列のスタート時間
              e = new Date (Date.UTC (1970,0,1,24, 30)),//列の終了時間
              s = new Date (Date.UTC (1970,0,1, 0,30));//列の感覚調整時間

            //schedule.setHoliday();の時コンソール結果が配列が空になっている。
            console.log(dayNo); 

            dayNo.forEach (n => {
              let current = new Date (start);

              current.setDate (7 - current.getDate () + n);

              for (; current < end; current.setDate (current.getDate () + 7)) {
                let idx = F (current);

                for (let c = b; c < e; c = new Date (+s + (+c))) {
                  let key = I (c);
                  if (! this.plan[key])
                    this.plan[key] = [ ]; //ここの配列が空によりエラーが起きている?
                  this.plan[key][idx] = true;
                }
              }
            });

            return this;
          }

    // 定休日と昼休みの設定
    //0:sun 1:mon 2:tue 3:wed 4:thu 5:fri 6:sat
    //setHolidayを設定しないとエラーが起きる。setHoliday()だとエラーになる。なんで?
    schedule.setHoliday(1).setLunchTime (0, 2, 4, 6);

    //クリック処理
    table.addEventListener('click', event => {
        const target = event.target;

        if (target.nodeName !== 'TD')
          return;

        if ('◎' !== target.textContent)
          return;

        let booktime = parseInt (document.querySelector ('#booktime').value, 10);
        //表の始まりの日付
        let cur = schedule.current;
        //クリック時の表の(セル)座標x
        let x = target.cellIndex;
        //クリック時の表の(セル)座標y
        let y = target.parentNode.rowIndex - 2;
        //クリックされた列の日付を取得
        let tgt = new Date (cur);
        tgt.setDate (tgt.getDate () + x -1);
        //その日のスケジュールを取得
        ////schedule.setHoliday();の時下記のコードでエラーが発生
        let plan = Schedule.getDayPlan (schedule, tgt);
        // console.log(plan);
        //plan から9時~15時までを抜き出す
        plan = plan.slice (18, 31);
        //予約コースの判定
        let rng = booktime / 30 |0;
        plan = plan.slice (y, y + rng);

        if (rng === plan.length && plan.every (a=> !a[1])){
          //console.log (rng ? 'ok': 'Invalid');
          var id = Array.from(document.getElementsByName('id'), x => x.value);

          if (!(target.closest('tbody') && 1 <= target.cellIndex && target.cellIndex <= 14)) {
              return true;
            }
            const
              time = target.parentNode.cells[0].textContent,
              table = target.closest('table'),
              date = table.tHead.rows[1].cells[target.cellIndex - 1].textContent.replace(/[^0-9]/g, ''),
              ymrow = table.tHead.rows[0],
              ym = (target.cellIndex <= ymrow.cells[1].colSpan ? ymrow.cells[1] : ymrow.cells[2]).textContent;

            let
              value = ym + date + '日' + time,
              param = document.getElementById( "booktime" ).value;
              //newに飛ぶ
              alert('エラーは取れましたね');
              //window.location.href = `new.${id}`+`confirm?datetime=${value + '-' + param + '分コース' + id}`;
        }else{
            alert("その時間帯は予約は出来ません。");
        }
      }, false);

問題の再現方法
通常処理
schedule.setHoliday(1)orschedule.setHoliday(1,3,6)とsetHolidayの中に、数字がある場合、カレンダー内の◎のセルをクリックすると「エラーは取れましたね」とアラートが表示されます。

エラーが起きる時
schedule.setHoliday()とsetHolidayの中に、数字がない場合、カレンダー内の◎のセルをクリックしてもアラートが表示されなくなります。

エラーが出る時のcodepenプロジェクト
エラー詳細

解決した方法

私の力ではどうしても分からなかったため、@hai_haiさんからご教示いただきました。
a = Array(24).fill(0).reduce((a,b,c)=>(b=('0'+c).slice(-2),[...a,b+':00',b+':30']),[]).reduce((a,b)=>(a[b]=Array(366).fill(false),a),{});

実際のコード

setHoliday (...dayNo) {
            let
              year   = this.current.getFullYear (),
              start  = new Date (year, 0, 1),
              end    = new Date (year, 12, 1),
              // ここでdayNoが0の時に実行される処理。全てを初期化する。this.planにaを代入する。
              a = Array(24).fill(0).reduce((a,b,c)=>(b=('0'+c).slice(-2),[...a,b+':00',b+':30']),[]).reduce((a,b)=>(a[b]=Array(366).fill(false),a),{}),
              b = new Date (Date.UTC (1970,0,1, 0, 0)),//列のスタート時間
              e = new Date (Date.UTC (1970,0,1,24, 30)),//列の終了時間
              s = new Date (Date.UTC (1970,0,1, 0,30));//列の感覚調整時間
              //setHolidayのdayNoに戻り値が返されるので、dayNoの中の配列が空かどうか確認し、空の場合のみにthis.planにaを代入する。
              if (!dayNo.length > 0){
                this.plan = a;
              }
            dayNo.forEach (n => {
              let current = new Date (start);

              current.setDate (7 - current.getDate () + n);

              for (; current < end; current.setDate (current.getDate () + 7)) {
                let idx = F (current);

                for (let c = b; c < e; c = new Date (+s + (+c))) {
                  let key = I (c);
                  if (! this.plan[key])
                    this.plan[key] = [ ];
                  this.plan[key][idx] = true;
                }
              }
            });

今後について

今回質問させていただくにあたり、ご回答して頂いた多数の方から身の丈にあったところから勉強していくべきとご指摘を受けたことを踏まえ、私もその通りだと感じました。
今後については、javascriptをちゃんと勉強した上で再度カレンダーの実装について取り組んでいきたいと思います。
何度も質問してしまいご迷惑をおかけして申し訳ございませんでした。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • kei344

    2019/04/10 13:07

    「setHolidayの中に、数字がない場合」のcodepenを別に用意してください。また、「クリックするとエラーになってしまう。」ではなくエラー文をそのまま提示してください。

    キャンセル

  • nanase21

    2019/04/10 19:14

    @hai_haiさん
    ご教示いただきありがとうございます。
    配列の指定値がundefinedの時、どのような処理を記述すればいいのでしょうか?

    キャンセル

  • nanase21

    2019/04/10 19:16

    @kei344さん
    質問内に数字がない場合ののcodepenを別に用意しました。

    キャンセル

回答 3

+4

原因:this.plan が初期化されていないから。
というか、配列の指定値が undefined でもそれを判断して動作するように工夫する。

既に上記のように指摘がありますが、
this.plan[キー][キー] をあらかじめfalseで埋めるか(初期化するか)、
return idx.map (i => [i, !!obj.plan[i][dn]]); の部分で obj.plan[i] が有るか無いかを判定して処理するか、
すればエラーは起こらなくなります。

下記部分はJavaScriptでは特に問題というわけではないですが、意図していないなら全角空白の判別できるエディタを使用されることをお勧めします。

/*                    全角スペース↓ */
for(let i=0;i<num.length;i++){/*   */
  schedule.setHoliday (num[i]);
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/20 21:54

    もちろん、this.plan です。
    今のところ、this.plan = [] と初期化されていますから、これを、
    this.plan = {"00:00":[false,false, ... ],"00:30":[false,false, ... ], ... }
    にするってことですね。

    キャンセル

  • 2019/04/21 00:41

    @Lhankor_Mhy さん
    this.planのかっこの中は30分ごとにfalseと書いていくのでしょうか?

    キャンセル

  • 2019/04/21 00:45

    To: Lhankor_Mhyさん
    出来れば回答して、そこのコメントでやり取りをお願いできませんか?

    キャンセル

checkベストアンサー

+1

最初に初期化する。

身の丈に合った仕事をすべきです。
「このコードをどこに、どのように埋め込めばよいのですか?」
というのなら、もうあきらめたほうがよい。
(1行で済むようなことを・・・)

let a = Array(24).fill(0).reduce((a,b,c)=>(b=('0'+c).slice(-2),[...a,b+':00',b+':30']),[]).reduce((a,b)=>(a[b]=Array(366).fill(false),a),{});

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/21 03:06

    ご教示頂きありがとうございます。
    一旦、カレンダーについては私の知識量ではこれ以上進めれそうにないのでjsについてちゃんと勉強してからアレンジを加えてみます。
    ご迷惑をおかけして申し訳ございませんでした。

    キャンセル

0

よくわからないコードは小さく分割して、理解できた部分から実装することをお勧めします。

何十行にもわたるコードをコピー&ペーストしてしまうと、どこでエラーが起きているのかわからなくて当然ですし、それを他人に解読させるのはなかなか困難だと思います。

コードのどの部分でエラーや期待していない動作が起きているかを自分で確認した上で、その部分だけを抽出したコードを乗せた方が有効な回答も得られやすいでしょう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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