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

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

新規登録して質問してみよう
ただいま回答率
85.50%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

1471閲覧

カレンダーの実装について: 予約が埋まっているところはクリックしても遷移出来ないようにしたい。

nanase21

総合スコア144

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/03/21 10:16

実現したいこと

予約がすでに埋まっている時間帯はその時間帯をクリックしても反応しないようにしたい。
例えば、12時が既に予約がされている時、新規の申込者が120分コースを選択した時に10時より前の時間を選択せず、10:30や11:00や11:30を選択しようとクリックした時に確認画面に飛ばないようにしたい。

###試した事
他の質問やネットの記事を参考にしてカレンダーをクリックすると,画面遷移し、パラメーターに値を乗せて遷移先のテキストフィールドに予約したい時間帯を反映させる事ができた。

###困っている事
予約が埋まっている時間を取得し、どのような処理を書けばいいか分からなく困っています。

動くサンプル

html

1<p>予約時間: <input type="text" name="booktime" value="120分" class= "booktime" id="booktime" readonly></p> 2 3 <p>選択した日時: <input type="text" class= "text" id="acdn-target" onselect="click_date" readonly></p> 4<div id="calendar"></div>

js

1{ 2 const 3 day_name = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'], 4 day_jname = ['(Sun.)', '(Mon.)', '(Tue.)', '(Wed.)', '(Thu.)', '(Fri.)', '(Sat.)'], 5 doc = document, 6 DEF_OPTION = { }; 7 8 const 9 A = (e, ...a) => a.map (a => e.appendChild (doc.createElement (a))), 10 B = (d, n) => d.setDate (d.getDate () + n), 11 C = d => [d.getFullYear (), '年', d.getMonth () + 1 , '月'].join (''), 12 D = d => [('0' + d.getHours ()).slice (-2), ('0' + d.getMinutes ()).slice (-2)].join (':'), 13 E = (e, d) => { 14 let 15 [a,,b] = A (e, 'label', 'br', 'label'), 16 w = d.getDay () ; 17 a.textContent = d.getDate (); 18 b.textContent = day_jname[w]; 19 e.classList.add (day_name[w]); 20 }, 21 F = d => { 22 let m = d.getMonth (); 23 return [0,31,59,90,120,151,181,212,243,273,304,334][m] + d.getDate() - 1 + 24 (new Date (d.getFullYear (), m + 1,0) === 29 && 0 < m); 25 }, 26 H = d => new Date (d.getFullYear (), d.getMonth (), d.getDate ()), 27 I = d => [('0' + d.getUTCHours ()).slice (-2), ('0' + d.getUTCMinutes ()).slice (-2)].join (':'); 28 29 //_____________ 30 31 32 class Shedule { 33 34 constructor (date = new Date, range = 7, plan = [ ], option = DEF_OPTION) { 35 this.current = H (date);//表の左端の日付、これを基準とする 36 this.date = H (date); 37 this.range = range; //何日分を表示するか 38 this.plan = plan; //予定表が構成される 39 this.table = document.createElement ('table'); 40 this.option = Object.assign ({ }, option); 41 42 this.remake (); 43 } 44 45 46 add (day = this.range) { 47 let d = H (this.current); 48 d.setDate (d.getDate () + day); 49 if (+this.date <= +d) 50 this.current = d; 51 return this; 52 } 53 54 55 setHoliday (...dayNo) { 56 let 57 year = this.current.getFullYear (), 58 start = new Date (year, 0, 1), 59 end = new Date (year, 12, 1), 60 61 b = new Date (Date.UTC (1970,0,1, 0, 0)),//列のスタート時間 62 e = new Date (Date.UTC (1970,0,1,24, 30)),//列の終了時間 63 s = new Date (Date.UTC (1970,0,1, 0,30));//列の感覚調整時間 64 65 dayNo.forEach (n => { 66 let current = new Date (start); 67 68 current.setDate (7 - current.getDate () + n); 69 70 for (; current < end; current.setDate (current.getDate () + 7)) { 71 let idx = F (current); 72 73 for (let c = b; c < e; c = new Date (+s + (+c))) { 74 let key = I (c); 75 if (! this.plan[key]) 76 this.plan[key] = [ ]; 77 this.plan[key][idx] = true; 78 } 79 } 80 }); 81 82 return this; 83 } 84 85 86 87 setLunchTime (...dayNo) { 88 let 89 year = this.current.getFullYear (), 90 start = new Date (year, 0, 1), 91 end = new Date (year, 12, 1), 92 93 b = new Date (Date.UTC (1970,0,1, 12, 0)),//列のスタート時間 94 e = new Date (Date.UTC (1970,0,1,13, 30)),//列の終了時間 95 s = new Date (Date.UTC (1970,0,1, 0,30));//列の感覚調整時間 96 97 dayNo.forEach (n => { 98 let current = new Date (start); 99 current.setDate (7 - current.getDate () + n); 100 101 for (; current < end; current.setDate (current.getDate () + 7)) { 102 let idx = F (current); 103 104 for (let c = b; c < e; c = new Date (+s + (+c))) { 105 let key = I (c); 106 if (! this.plan[key]) 107 this.plan[key] = [ ]; 108 this.plan[key][idx] = true; 109 } 110 } 111 }); 112 113 return this; 114 } 115 116 117 //Booking 118 setBook (date, occupancyTime) {// 予約日時,占有時間(分) 119 let 120 current = new Date (date), 121 dayNo = F (current), 122 sTime = 30,//30間隔 123 step = new Date (Date.UTC (1970,0,1, 0, sTime)), 124 cnt = Math.floor (occupancyTime / sTime); 125 126 for (let i = 0; i < cnt; i++) { 127 let time = D (current); 128 if (! this.plan[time]) 129 this.plan[time] = [ ]; 130 this.plan[time][dayNo] = true; 131 current.setTime (+current +(+step)); 132 } 133 return this; 134 } 135 136 remake () { 137 this.prev_btn = this.next_btn = null; 138 let t = this.table; 139 [...t.childNodes].forEach (e => e.remove ()); 140 141 let 142 h = t.createTHead (), 143 tr0 = h.insertRow (-1), 144 tr1 = h.insertRow (-1), 145 d = H (this.current), 146 s = tr0.insertCell (-1), 147 c = 1, 148 m = d.getMonth (); 149 150 for (let i = 0; i < this.range; i++, c++, B (d, 1)) { 151 let td = tr1.insertCell (-1); 152 if (m !== d.getMonth ()) { 153 s.colSpan = c - 1; 154 s = tr0.insertCell (-1); 155 m = d.getMonth (); 156 c = 1; 157 } 158 s.textContent = C (d); 159 E (td, d); 160 } 161 s.colSpan = c - 1; 162 163 let e = tr0.insertCell (0); 164 e.textContent = '前の一週間'; 165 e.classList.add ('button'); 166 e.rowSpan = 2; 167 this.prev_btn = e; 168 169 e = tr0.insertCell (-1); 170 e.textContent = '次の一週間'; 171 e.classList.add ('button'); 172 e.rowSpan = 2; 173 this.next_btn = e; 174 175 //__ 176 let 177 begin = new Date (Date.UTC (1970,0,1, 9, 0)),//列のスタート時間 178 end = new Date (Date.UTC (1970,0,1,15, 30)),//列の終了時間 179 step = new Date (Date.UTC (1970,0,1, 0,30)),//列の感覚調整時間 180 o = F (this.current), 181 rst = [ ]; 182 183 for (let d = begin; d < end; d = new Date (+step + (+d))) { 184 let 185 a = new Array (this.rangi), 186 b = I (d), 187 c = this.plan[b] || []; 188 189 for (let i = 0; i < this.range; i++) 190 a[i] = c[o + i] ? '-' : '◎'; 191 192 rst.push ([b, ...a, b]); 193 } 194 195 let [tb] = A (t, 'tbody'); 196 rst.forEach (r => { 197 let tr = tb.insertRow (-1); 198 r.forEach (c => tr.insertCell (-1).textContent = c) 199 }); 200 201 return this; 202 } 203 204 205 handleEvent (event) { 206 let t = event.target; 207 if (t === this.prev_btn) this.add (-this.range).remake (); 208 if (t === this.next_btn) this.add ( this.range).remake (); 209 210 let cbfunc = this.option.handleEvent; 211 if ('function' === typeof cbfunc) 212 cbfunc.call (this, event); 213 } 214 215 216 static convert (ary) { 217 let rst = { }; 218 ary.forEach (a => { 219 let 220 dt = new Date (a + ':00.000+09:00'), 221 tm = D (dt), 222 dn = F (dt); 223 if (! rst[tm]) 224 rst[tm] = []; 225 rst[tm][dn] = true; 226 }); 227 return rst; 228 } 229 230 } 231 232 this.Shedule = Shedule; 233} 234 235//_____________ 236function strDateJp (date) { 237 let [y, m, d] = ['getFullYear', 'getMonth', 'getDate'].map (fc => date[fc]()); 238 return `${y}${m+1}${d}`; 239} 240 241 242//___________________________ 243 244const 245 BUSY = ['2019-02-19T14:30', '2019-02-21T14:30'], 246 plan = Shedule.convert (BUSY), 247 shedule = new Shedule (new Date, 7, plan), 248 table = shedule.table; 249 250table.border = 1; 251//月水金を休日      昼休みがあるのが日火木土 252shedule.setHoliday (0, 6).setLunchTime (0, 1, 2, 3, 5, 4, 6)//..remake (); 253 254 //予約入った時 例) 2月24日 9時30 2時間コースと予約が入った時 255shedule.setBook (new Date (2019, 1, 19, 9, 30), 30).remake (); 256shedule.setBook (new Date (2019, 1, 24, 9, 30), 120).remake (); 257 258//shedule.remake (); //単独で表を更新 259document 260 .querySelector ('#calendar') 261 .appendChild (table) 262 .addEventListener ('click', shedule, false); 263 264//クリック時の処理 265document.getElementById ('acdn-target').value = strDateJp (shedule.current); 266 267document.querySelector('#calendar').addEventListener('click', event => { 268 const target = event.target; 269 if (target.nodeName.toLowerCase() !== 'td') { 270 return true; 271 } 272 273 if (!(target.closest('tbody') && 1 <= target.cellIndex && target.cellIndex <= 7)) { 274 return true; 275 } 276 277 const time = target.parentNode.cells[0].textContent; 278 const table = target.closest('table'); 279 const date = table.tHead.rows[1].cells[target.cellIndex - 1].textContent.slice(0,-6); 280 const ymrow = table.tHead.rows[0]; 281 const ym = (target.cellIndex <= ymrow.cells[1].colSpan ? ymrow.cells[1] : ymrow.cells[2]).textContent; 282 document.getElementById('acdn-target').value = ym + date + '-' + time; 283 284 const value = ym + date + ' ' + time; 285 var id = Array.from(document.getElementsByName('id'), x => x.value); 286 //確認ページに飛ぶ 287 window.location.href = `new.${id}`+`confirm?datetime=${value}`; 288 289 //ここで予約時間を取得 290 var str = document.getElementById( "booktime" ).value, 291 edit_str = str.slice(0,-1); 292 console.log(edit_str); 293 294 295 return true; 296}, false);

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

まだまだ続くのですね
ヒントだけ提示します
(風邪で寝込んで、はや1週間)

以下を参考にして getDayPlan を追加します。挿入位置に注意!!

javascript

1 static getDayPlan (obj, dt) { 2 let idx = [ ]; 3 let dn = F (dt); 4 for (let i = 0; i < 24; i+= 1) { 5 let h = ('0' + i).slice (-2); 6 idx.push (h + ':00'); 7 idx.push (h + ':30'); 8 } 9 return idx.map (i => [i, !!obj.plan[i][dn]]); 10 } 11 12 } 13 14 this.Shedule = Shedule; 15} 16

追加2というかクリック処理部分

javascript

1table.addEventListener('click', event => { 2 const target = event.target; 3 4 if (target.nodeName !== 'TD') 5 return; 6 7 if ('◎' !== target.textContent) 8 return; 9 10 //表の始まりの日付 11 let cur = shedule.current; 12 //クリック時の表の(セル)座標x 13 let x = target.cellIndex; 14 //クリック時の表の(セル)座標y 15 let y = target.parentNode.rowIndex - 2; 16 //クリックされた列の日付を取得 17 let tgt = new Date (cur); 18 tgt.setDate (tgt.getDate () + x -1); 19 //その日のスケジュールを取得 20 let plan = Shedule.getDayPlan (shedule, tgt); 21 //plan から9時~15時までを抜き出す 22 plan = plan.slice (18, 31); 23 24 //必要なパラメータはそろった 25 console.log (tgt, plan, y); 26}, false); 27

投稿2019/03/21 22:39

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2019/03/22 01:06

体調不良の中でご教示いただきありがとうございます。ご迷惑をおかけして申し訳ございませんでした。 ご教示いただいたコードでイメージしていた実装をできました。 それと、ご教示をいただいたコードを元に例えば既に12:00〜13:00の箇所が予約されている場合、新規のユーザーが60分コースを選択した時、11:30が◎になっていてもクリック時に遷移できないようにしたいのですが、その場合どのようにしたら良いかアドバイスを頂けないでしょうか? //ここで予約時間を取得 var str = document.getElementById( "booktime" ).value, edit_str = str.slice(0,-1); console.log(edit_str); こんな感じでテキストフィールドの値を引っ張ってくるところまでは出来ているのですが、その後の処理をどのように書けばよいか分からずにいます.... デバッグしてみると、予約されている時間帯はtrueになっているので予約した時間でコース時間がtrueの配列の値が含まれる時遷移できなくすればいいのではないかと試してみたものも出来ませんでした。
退会済みユーザー

退会済みユーザー

2019/03/22 01:25

「その後の処理をどのように・・・」、そこが今回の質問の「肝」なのだから少しは努力しましょう! booktime を30分で割ると4、y座標から4個分、配列plan に空きがあるか?だよね。
guest

0

面倒なので短く書きました。
紐解かないまま次ばかり追うと、結局何をしているのかわからまいまま。

javascript

1table.addEventListener('click', event => { 2 const target = event.target; 3 4 if (target.nodeName !== 'TD') 5 return; 6 7 if ('◎' !== target.textContent) 8 return; 9 10 let booktime = parseInt (document.querySelector ('#booktime').value, 10); 11 let cur = shedule.current; 12 let x = target.cellIndex; 13 let y = target.parentNode.rowIndex - 2; 14 let tgt = new Date (cur); 15 tgt.setDate (tgt.getDate () + x - 1); 16 let plan = Shedule.getDayPlan (shedule, tgt).slice (18, 31); 17 let rng = booktime / 30 |0; 18 19 plan = plan.slice (y, y + rng); 20 let r = rng === plan.length && plan.every (a=> !a[1]); 21 22 console.log (r ? 'ok': 'Invalid'); 23 24}, false); 25

投稿2019/03/22 05:30

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2019/03/22 13:30

ご教示いただきありがとうございました。 少しは自分で解決するよう、ご指摘いただいていたので私になりに別な方法でアプローチしていたのですが中々思うようにいかずに困っていたのでとても助かりました。 私のやろうとしていた方法としては、inputの時間を同じにように30で割った後に、while文で一個一個配列を探索して全てがfalseだったら遷移し、trueがあればalertが表示されるように長々と書いていたのですがこんなにもシンプルに書けたんですね。 とても勉強になりました。
nanase21

2019/03/23 01:37

何度もご質問してしまい申し訳ございません。 質問の内容が異なるので新規に質問を投稿したので、お時間あるときにご教示いただけないでしょうか? Qiitaの記事を参考にjsでメディアクエリを書いてみたのですが、そうすると途端にカレンダーが表示されなくなってしまいました。 やりたい事としては、スマホサイズの時は表示されるカレンダーが1週間でタブレットでは10日間、pcでは2週間としたいのですが、どうも上手くいかず困っています。 https://teratail.com/questions/180841?modal=q-comp
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問