--- tags: Example, disqus: hackmd --- # 台灣高鐵 API練習 [台灣高鐵...](https://www.thsrc.com.tw/index.html) [API位置](https://ptx.transportdata.tw/MOTC?t=Rail&v=2#/) ### 本次練習目標 1.製作能看到各車站的基本資料。 2.製作票價表:所有票價、選擇的起訖站票價、各車廂票價。 3.製作時刻表:單一條件搜尋或交叉搜尋日期、起訖站、車次代碼,並顯示票價。 ### 製作想法 預計製作三個頁面分別是基本資料、票價表、時刻表,因此我打算使用三個閉包來放置個頁面的code,方便以後維護或新增,然後匯聚至一個閉包做統整與執行。 ### 製作上遇到的問題 1.條件篩選 以往都是將API資料取回再直接做顯示,這是第一次製作多條件的顯示,把資料拿回來後要再根據條件做篩選。 2.該使用哪隻API 面對眾多的API我該如何選擇,以及在考慮到我的多條件,我應該要使用哪些資料。 ### 心得 本次的製作,儘管已經在開始前有先構思過一番設計結構,但在製作中還是有遇到API要在統整處再帶入,還是放在各頁閉包的掙扎。 以及本次的條件搜尋,並非完全像台灣高鐵官網的需選擇起訖站、日期、時間才能搜尋。 而是把日期設為必填,有日期+起訖站、日期+車次、日期+起訖站+車次的搜尋選擇。 ```htmlmixed= <html> <head> <title>台灣高鐵 - ajax作業</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="jquery-3.5.1.min.js"></script> <link rel="stylesheet" href="index.css"> </head> <body> <div class="page-body"> <h2 class="body-title">台灣高鐵</h2> <div class="nav-wrap"> <div id="nav-station" class="nav-item active" data-index="0">車站資訊</div> <div id="nav-price" class="nav-item" data-index="1">票價表</div> <div id="nav-time" class="nav-item" data-index="2">時刻表</div> </div> <div id="station-board" class="content-wrap active"> <h3 class="station-title">車站資訊</h3> <div id="station-nav" class="clearfix"></div> <div class="station-content clearfix"> <div class="station-content-wrap"> <p id="station-cname" class="station-cname"></p> <p id="station-ename" class="station-ename"></p> <p id="station-address" class="station-address"></p> </div> <img id="station-image" class="station-image" src="" alt=""> </div> </div> <div id="price-board" class="content-wrap"> <div class="type-wrap"> <p class="type-button active" data-type="4">所有票價</p> <p class="type-button" data-type="5">起訖站票價</p> <p class="type-button" data-type="0">商務票價</p> <p class="type-button" data-type="1">標準票價</p> <p class="type-button" data-type="2">自由票價</p> </div> <div id="all-table" class="price-wrap active"> <div class="all-table-content"> <h3 class="price-title">商務票價</h3> </div> <div class="all-table-content"> <h3 class="price-title">標準票價</h3> </div> <div class="all-table-content"> <h3 class="price-title">自由票價</h3> </div> </div> <div id="level-table" class="price-wrap"></div> <div id="select-table" class="price-wrap"> <div class="spot-wrap"> <div class="spot-content"> <p>起點站</p> <select id="select-start"></select> </div> <div class="spot-content"> <p>終點站</p> <select id="select-end"></select> </div> </div> <div id="select-journey" class="select-journey"></div> <div id="select-price"></div> </div> </div> <div id="time-board" class="content-wrap"> <h3 class="time-title">時刻表與票價查詢</h3> <div class="query-wrap"> <p class="query-title">請選擇查詢條件</p> <div class="query-rule-wrap"> <input type="date" id="query-date" class="query-date" name="query-date"> <select id="query-start" class="query-rule"></select> <select id="query-end" class="query-rule"></select> <select id="query-trip" class="query-rule"></select> </div> <div id="query-button" class="query-button">立即查詢</div> </div> <div id="time-table-wrap"> <h3 id="time-table-title" class="time-table-title"></h3> <div class="time-table"> <div id="time-table-head" class="time-table-head"></div> <div id="time-table-body" class="time-table-body"></div> </div> <h3 class="time-table-title">車廂票價參考</h3> <div id="time-price-wrap" class="time-price-wrap"></div> </div> </div> </div> <script src="index.js"></script> </body> </html> ``` ```javascript= /* 立即函示 + 閉包 */ (function() { function hasClass(el, className) { return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)')); } function addClass(el, className) { if (!hasClass(el, className)) { el.className += " " + className; } } function removeClass(el, className) { if (hasClass(el, className)) { var reg = new RegExp('(\\s|^)' + className + '(\\s|$)'); el.className=el.className.replace(reg, ' '); } } function altFind(arr, callback) { for (var i = 0; i < arr.length; i++) { var match = callback(arr[i], i); if (match) { return arr[i]; } } } function railStation() { var stationNav = document.getElementById('station-nav'); var stationItem = document.getElementsByClassName('station-item'); var stationCname = document.getElementById('station-cname'); var stationEname = document.getElementById('station-ename'); var stationAddress = document.getElementById('station-address'); var stationImage = document.getElementById('station-image'); var copyArray = []; /* 複製取回資料的陣列並處理 */ var renderArray = []; /* 渲染陣列 */ var stationArray = []; /* 各站 */ var imageArray = [ { id: '0990', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/55d8ad33-063d-47ab-831b-06779a2e49dd.jpg' }, { id: '1000', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/02a8e78d-c8b1-4c13-9fb6-4095167fd0d8.jpg' }, { id: '1010', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/08f71160-6772-4663-8bb0-9be4e6bae343.jpg' }, { id: '1020', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/c90e3a8a-1598-46de-a565-31f017442477.jpg' }, { id: '1030', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/e69a00c8-0fc4-48cb-87cd-68a20f86d6ea.jpg' }, { id: '1035', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/3614c021-5756-480b-9615-b6133e6fbe74.jpg' }, { id: '1040', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/a7afd699-301a-4744-886b-f476c7a96bef.jpg' }, { id: '1043', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/2ee9fcdb-b4a4-4521-b037-c9aecac26119.jpg' }, { id: '1047', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/30ed9976-3fd2-4467-afaf-2b2d45a9c988.jpg' }, { id: '1050', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/e1d5d3c1-68ed-4c7b-994b-6b4294f65649.jpg' }, { id: '1060', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/9dafd11f-d20b-45bf-be9a-e000b14cc086.jpg' }, { id: '1070', path: 'https://www.thsrc.com.tw/UploadFiles/StationInfo/cc9215d5-a252-4168-af6e-1a837c1d5f2a.jpg' } ]; var apiObject = { handleItemClick: function() { for (var index = 0; index < stationItem.length; index++) { const element = stationItem[index]; element.addEventListener('click', function() { var id = element.getAttribute('data-id'); Array.prototype.forEach.call(stationItem, function(ele) { removeClass(ele, 'active'); }); addClass(element, 'active'); apiObject.handleContentRender(id); }); } }, handleContentRender: function(id) { var targetItem = altFind(renderArray, function(ele) { return ele.id === id; }); stationCname.innerHTML = targetItem.cName + '站'; stationEname.innerHTML = targetItem.eName; stationAddress.innerHTML = targetItem.address; stationImage.setAttribute('src', targetItem.imagePath); }, handleNavRender: function() { /* 處理初次渲染 */ var structureDom = ''; renderArray.map(function(ele) { var item = '<div class="station-item" data-id="'+ ele.id +'">'+ ele.cName +'</div>'; structureDom = structureDom + item; }); stationNav.innerHTML = structureDom; stationCname.innerHTML = renderArray[0].cName + '站'; stationEname.innerHTML = renderArray[0].eName; stationAddress.innerHTML = renderArray[0].address; stationImage.setAttribute('src', renderArray[0].imagePath); addClass(stationItem[0], 'active'); this.handleItemClick(); }, handleData: function(responseText) { /* 當api成功回傳,處理api回傳資料 */ responseData = JSON.parse(responseText); /* 未來對方API欄位名稱變更,只需修改此處 */ responseData.map(function(element) { var imgPath = ''; altFind(imageArray, function(image) { if (element.StationID === image.id) { imgPath = image.path; } }); copyArray.push({ id: element.StationID, address: element.StationAddress, cName: element.StationName.Zh_tw, eName: element.StationName.En, imagePath: imgPath }); }); /* 使用jQuery的$.extend做copy array */ renderArray = $.extend(true, [], copyArray); /* 取出各站中文名 */ renderArray.map(function(ele) { stationArray.push(ele.cName); }) this.handleNavRender(); }, callApi: function() { if (copyArray.length === 0) { $.ajax({ method: 'GET', url: 'https://ptx.transportdata.tw/MOTC/v2/Rail/THSR/Station?$format=JSON', dataType: 'text' }) .done(function(data) { apiObject.handleData(data); }); } }, execute: function() { this.callApi(); } } return apiObject; } function priceTable() { var typeButton = document.getElementsByClassName('type-button'); var allTableContent = document.getElementsByClassName('all-table-content'); var priceWrap = document.getElementsByClassName('price-wrap'); var levelTable = document.getElementById('level-table'); var allTable = document.getElementById('all-table'); var selectTable = document.getElementById('select-table'); var selectStart = document.getElementById('select-start'); var selectEnd = document.getElementById('select-end'); var selectPrice= document.getElementById('select-price'); var selectJourney = document.getElementById('select-journey'); var copyArray = []; /* 複製取回資料的陣列並處理 */ var renderArray = []; /* 渲染陣列 */ var stationArray = []; /* 各站id陣列 */ var faresLength = 3; /* 票價種類 */ var apiObject = { handleType: function(type) { Array.prototype.forEach.call(priceWrap, function(ele) { removeClass(ele, 'active'); }); switch (type) { case 4: addClass(allTable, 'active'); break; case 5: addClass(selectTable, 'active'); break; default: apiObject.handleCreateLevelTable(type); addClass(levelTable, 'active'); break; } }, handleTypeButton: function() { var type = ''; for (var index = 0; index < typeButton.length; index++) { typeButton[index].addEventListener('click', function() { Array.prototype.forEach.call(typeButton, function(ele) { removeClass(ele, 'active'); }); addClass(this, 'active'); type = this.getAttribute('data-type'); apiObject.handleType(parseInt(type, 10)); }); } }, handleRenderSelect: function(journey) { /* 起訖站票價跟地點 */ selectJourney.innerText = journey.startCName +'到'+ journey.endCName; selectPrice.innerHTML = '<p class="select-price-item">商務票價格為:'+ journey.fares[0].price +'</p>' + '<p class="select-price-item">標準票價格為:'+ journey.fares[1].price +'</p>' + '<p class="select-price-item">自由票價格為:'+ journey.fares[2].price +'</p>' }, handleSelectTableChange: function() { /* 起訖站下拉選單事件 */ var startSpot = ''; var endSpot = ''; var journey = []; selectStart.addEventListener('change', function() { startSpot = this.value; if (this.value === '0') { return; } if (!endSpot || endSpot === '0') { journey = renderArray.filter(function(ele) { return ele.startId === startSpot; }); return; } if (selectStart.value === selectEnd.value) { alert('起點站/終點站 不能相同'); } else { journey = altFind(renderArray, function(ele) { return ele.startId === startSpot && ele.endId === endSpot; }); apiObject.handleRenderSelect(journey); } }); selectEnd.addEventListener('change', function() { endSpot = this.value; if (this.value === '0') { return; } if (!startSpot || startSpot === '0') { journey = renderArray.filter(function(ele) { return ele.endId === endSpot; }); return; } if (selectStart.value === selectEnd.value) { alert('起點站/終點站 不能相同'); } else { journey = altFind(renderArray, function(ele) { return ele.startId === startSpot && ele.endId === endSpot; }); apiObject.handleRenderSelect(journey); } }); }, handleCreateSelectTable: function() { /* 起訖站票價 */ var tmpStartDom = '<option value="0">請選擇起點</option>'; var tmpEndDom = '<option value="0">請選擇終點</option>'; stationArray.map(function(ele) { tmpStartDom = tmpStartDom + '<option value="'+ ele.id +'">'+ ele.cName +'</option>'; tmpEndDom = tmpEndDom + '<option value="'+ ele.id +'">'+ ele.cName +'</option>'; }); selectStart.innerHTML = tmpStartDom; selectEnd.innerHTML = tmpEndDom; this.handleSelectTableChange(); }, handleCreateLevelTable: function(type) { /* 個別票價 */ var tmpDom = ''; stationArray.forEach(function(ele) { var price = ''; ele.list.forEach(function(item){ price = price + '<div class="price-item"><div>'+ item.endCName +'</div><div class="price-dolor">'+ item.fares[type].price +'</div></div>'; }); tmpDom = tmpDom + '<div class="price-box"><div class="price-item">'+ ele.cName +'</div>'+ price +'</div>'; }); levelTable.innerHTML = tmpDom; }, handleCreateAllTable: function() { /* 所有票價 */ for (var index = 0; index < faresLength; index++) { var tmpDom = allTableContent[index].innerHTML; stationArray.forEach(function(ele) { var price = ''; ele.list.forEach(function(item){ price = price + '<div class="price-item"><div>'+ item.endCName +'</div><div class="price-dolor">'+ item.fares[index].price +'</div></div>'; }); tmpDom = tmpDom + '<div class="price-box"><div class="price-item">'+ ele.cName +'</div>'+ price +'</div>'; }); allTableContent[index].innerHTML = tmpDom; } }, handleStation: function() { var tmpArray = []; renderArray.forEach(function(ele) { tmpArray.push(ele.startId); }); stationArray = tmpArray.filter(function(element, index, arr) { return arr.indexOf(element) === index; }).sort(); stationArray.forEach(function(ele, index) { stationArray.splice(index, 1, { id: ele, list: [] }); }); renderArray.map(function(ele) { altFind(stationArray, function(station, index) { if (ele.startId === station.id) { if (!stationArray[index].cName) { stationArray[index].cName = ele.startCName; } stationArray[index].list.push(ele); stationArray[index].list.sort(function(a, b) { return a.endId - b.endId; }); } }); }); }, handleData: function(responseText) { /* 當api成功回傳,處理api回傳資料 */ responseData = JSON.parse(responseText); /* 未來對方API欄位名稱變更,只需修改此處 */ responseData.map(function(element) { var faresRenameArray = []; var faresSortArray = element.Fares.sort(function(a, b) { if (a.TicketType < b.TicketType) { return -1; } if (a.TicketType > b.TicketType) { return 1; } return 0; }); faresSortArray.forEach(function(ele) { faresRenameArray.push({ type: ele.TicketType, price: ele.Price }); }); copyArray.push({ startId: element.OriginStationID, endId: element.DestinationStationID, startCName: element.OriginStationName.Zh_tw, startEName: element.OriginStationName.En, endCName: element.DestinationStationName.Zh_tw, endEName: element.DestinationStationName.En, fares: faresRenameArray }); }); /* 使用jQuery的$.extend做copy array */ renderArray = $.extend(true, [], copyArray); renderArray.sort(function(a, b) { return a.startId - b.startId; }); this.handleStation(); this.handleCreateAllTable(); this.handleCreateSelectTable(); }, execute: function() { this.handleTypeButton(); } } return apiObject; } function timeTable() { var queryStart = document.getElementById('query-start'); var queryEnd = document.getElementById('query-end'); var queryDate = document.getElementById('query-date'); var queryTrip = document.getElementById('query-trip'); var queryButton = document.getElementById('query-button'); var timeTableTitle = document.getElementById('time-table-title'); var timeTableHead = document.getElementById('time-table-head'); var timeTableBody = document.getElementById('time-table-body'); var timePriceWrap = document.getElementById('time-price-wrap'); var queryRule = document.getElementsByClassName('query-rule'); var copyArray = []; /* 複製取回資料的陣列並處理 */ var renderArray = []; /* 渲染陣列 */ var stationArray = []; /* 各站id陣列 */ var tripArray = []; /* 所有車次陣列 */ var startStationId = '0'; var endStationId = '0'; var apiObject = { handleRenderTrip: function(date, trip) { /* 產生指定日期車次的時刻表 */ var tripDom = ''; var startCName = ''; var endCName = ''; var tripStartId = ''; var tripEndId = ''; var fares = []; altFind(tripArray, function(ele) { if (ele.trainNumber === trip) { tripStartId = ele.startId; tripEndId = ele.endId; startCName = ele.startCName; endCName = ele.endCName; ele.stopTimes.map(function(stopTime) { var stationCName = '<p>' + stopTime.StationName.Zh_tw + '</p>'; var departureTime = '<p>' + stopTime.DepartureTime + '</p>'; tripDom = tripDom + '<div class="item-trip">' + stationCName + departureTime + '</div>'; }); } }); altFind(stationArray, function(startStation) { if (tripStartId === startStation.id) { altFind(startStation.list, function(endStation) { if (tripEndId === endStation.endId) { fares = endStation.fares; } }); } }); timeTableTitle.innerHTML = '<div>' + date + '的' + trip + '車次' + '</div><div>從' + startCName + '站出發到' + endCName + '站</div>'; timeTableHead.innerHTML = '<div class="item-table">各站出發時間</div>'; timeTableBody.innerHTML = '<div>' + tripDom + '</div>'; timePriceWrap.innerHTML = '<div class="price-tr">商務票價格:'+ fares[0].price +'</div>' + '<div class="price-tr">標準票價格:'+ fares[1].price +'</div>' + '<div class="price-tr">自由票價格:'+ fares[2].price +'</div>'; }, handleRenderTable: function(date, startId, endId, tmpArray) { /* 產生指定日期起訖站的時刻表 */ var startCName = ''; var endCName = ''; var fares = []; var tbodyDom = ''; altFind(stationArray, function(ele) { if (ele.id === startId) { startCName = ele.cName; altFind(ele.list, function(eleList) { if (eleList.endId === endId) { endCName = eleList.endCName; fares = eleList.fares; } }); } }); tmpArray.map(function(ele) { var trainNumber = '<div class="item-table">' + ele.trainNumber + '</div>'; var departureTime = '<div class="item-table">' + ele.departureTime + '</div>'; var arrivalTime = '<div class="item-table">' + ele.arrivalTime + '</div>'; tbodyDom = tbodyDom + '<div>' + trainNumber + departureTime + arrivalTime + '</div>'; }); timeTableTitle.innerHTML = date + ' ' + startCName + '站 到 ' + endCName + '站'; timeTableHead.innerHTML = '<div class="item-table">車次</div><div class="item-table">出發時間</div><div class="item-table">到站時間</div>'; timeTableBody.innerHTML = tbodyDom; timePriceWrap.innerHTML = '<div class="price-tr">商務票價格:'+ fares[0].price +'</div>' + '<div class="price-tr">標準票價格:'+ fares[1].price +'</div>' + '<div class="price-tr">自由票價格:'+ fares[2].price +'</div>'; }, handleQuery: function() { var startId = queryStart.value; var endId = queryEnd.value; var date = queryDate.value; var trip = queryTrip.value; if (!queryDate.value) { return alert('請選擇日期'); } if (queryStart.value !== '0' && queryEnd.value !== '0') { if (queryStart.value === queryEnd.value) { return alert('起點/終點 不能是同一個點'); } if (trip !== '0') { apiObject.callDateDestination(date, startId, endId, trip); } else { apiObject.callDateDestination(date, startId, endId); } return; } if (queryStart.value !== '0' || queryEnd.value !== '0') { return alert('請選擇 起點/終點'); } if (trip && trip !== '0') { apiObject.handleRenderTrip(date, trip); } }, handleTripSelect: function(tripDataArray) { /* 車次下拉選單 */ queryTrip.innerHTML = ''; var optionDom = '<option value="0">請選擇車次</option>'; tripDataArray.map(function(ele) { optionDom = optionDom + '<option value="' + ele.trainNumber + '">' + ele.trainNumber + '</option>' }); queryTrip.innerHTML = optionDom; }, handleDate: function() { Date.prototype.addDays = function(days) { this.setDate(this.getDate() + days); return this; } var today = new Date(); var limitDay = new Date().addDays(28); var transToday = today.toISOString().substr(0, 10); var transLimitDay = limitDay.toISOString().substr(0, 10); // queryDate.defaultValue = transToday; queryDate.setAttribute('min', transToday); queryDate.setAttribute('max', transLimitDay); /* 使用預設當日取得當日所有車次的時刻表資料 */ this.callDate(transToday); }, handleStationSelect: function() { /* 起訖站下拉選單 */ var tmpStartDom = '<option value="0">請選擇起點</option>'; var tmpEndDom = '<option value="0">請選擇終點</option>'; stationArray.map(function(ele) { tmpStartDom = tmpStartDom + '<option value="'+ ele.id +'">'+ ele.cName +'</option>'; tmpEndDom = tmpEndDom + '<option value="'+ ele.id +'">'+ ele.cName +'</option>'; }); queryStart.innerHTML = tmpStartDom; queryEnd.innerHTML = tmpEndDom; }, handleStation: function() { var tmpArray = []; renderArray.forEach(function(ele) { tmpArray.push(ele.startId); }); stationArray = tmpArray.filter(function(element, index, arr) { return arr.indexOf(element) === index; }).sort(); stationArray.forEach(function(ele, index) { stationArray.splice(index, 1, { id: ele, list: [] }); }); renderArray.map(function(ele) { altFind(stationArray, function(station, index) { if (ele.startId === station.id) { if (!stationArray[index].cName) { stationArray[index].cName = ele.startCName; } stationArray[index].list.push(ele); stationArray[index].list.sort(function(a, b) { return a.endId - b.endId; }); } }); }); }, handleBindEvent: function() { queryButton.addEventListener('click', this.handleQuery); queryStart.addEventListener('change', function() { var date = queryDate.value; startStationId = this.value; /* 篩選符合起訖站之車次下拉選單 */ if (endStationId !== '0' && startStationId !== '0') { apiObject.callDateDestination(date, startStationId, endStationId, '0'); } else if (endStationId === '0' && startStationId === '0') { apiObject.handleTripSelect(tripArray); } }); queryEnd.addEventListener('change', function() { var date = queryDate.value; endStationId = this.value; /* 篩選符合起訖站之車次下拉選單 */ if (startStationId !== '0' && endStationId !== '0') { apiObject.callDateDestination(date, startStationId, endStationId, '0'); } else if (endStationId === '0' && startStationId === '0') { apiObject.handleTripSelect(tripArray); } }); queryDate.addEventListener('change', function() { var date = queryDate.value; if (!!date) { Array.prototype.forEach.call(queryRule, function(ele) { addClass(ele, 'show'); }); } if (startStationId !== '0' && endStationId !== '0') { apiObject.callDateDestination(date, startStationId, endStationId, '0'); } else { apiObject.callDate(this.value); } }); }, handleData: function(responseText) { /* 當api成功回傳,處理api回傳資料 */ responseData = JSON.parse(responseText); /* 未來對方API欄位名稱變更,只需修改此處 */ responseData.map(function(element) { var faresRenameArray = []; var faresSortArray = element.Fares.sort(function(a, b) { if (a.TicketType < b.TicketType) { return -1; } if (a.TicketType > b.TicketType) { return 1; } return 0; }); faresSortArray.forEach(function(ele) { faresRenameArray.push({ type: ele.TicketType, price: ele.Price }); }); copyArray.push({ startId: element.OriginStationID, endId: element.DestinationStationID, startCName: element.OriginStationName.Zh_tw, startEName: element.OriginStationName.En, endCName: element.DestinationStationName.Zh_tw, endEName: element.DestinationStationName.En, fares: faresRenameArray }); }); /* 使用jQuery的$.extend做copy array */ renderArray = $.extend(true, [], copyArray); renderArray.sort(function(a, b) { return a.startId - b.startId; }); this.handleStation(); this.handleStationSelect(); this.handleDate(); this.handleBindEvent(); }, callDateDestination: function(date, startId, endId, trip) { /* 取得指定[日期],[起迄站間]之時刻表資料 */ $.ajax({ method: 'GET', url: 'https://ptx.transportdata.tw/MOTC/v2/Rail/THSR/DailyTimetable/OD/'+ startId +'/to/'+ endId +'/'+ date +'?$format=JSON', dataType: 'text' }) .done(function(data) { var tmpArray = [] responseData = JSON.parse(data); responseData.map(function(ele) { tmpArray.push({ trainNumber: ele.DailyTrainInfo.TrainNo, departureTime: ele.OriginStopTime.DepartureTime, arrivalTime: ele.DestinationStopTime.ArrivalTime }); }); tmpArray.sort(function (a, b) { if (a.departureTime < b.departureTime) { return -1; } if (a.departureTime > b.departureTime) { return 1; } return 0; }); if (trip !== '0') { altFind(tmpArray, function(ele) { if (ele.trainNumber === trip) { tmpArray=[]; tmpArray.push(ele); } }); apiObject.handleRenderTable(date, startId, endId, tmpArray); } else if (trip === '0') { apiObject.handleTripSelect(tmpArray); } }); }, callDate: function(date) { /* 取得指定[日期]所有車次的時刻表資料(高鐵提供近28天每日時刻表) */ $.ajax({ method: 'GET', url: 'https://ptx.transportdata.tw/MOTC/v2/Rail/THSR/DailyTimetable/TrainDate/'+ date +'?$format=JSON', dataType: 'text' }) .done(function(data) { var tmpArray = []; responseData = JSON.parse(data); responseData.map(function(ele) { tmpArray.push({ trainNumber: ele.DailyTrainInfo.TrainNo, startId: ele.DailyTrainInfo.StartingStationID, endId: ele.DailyTrainInfo.EndingStationID, startCName: ele.DailyTrainInfo.StartingStationName.Zh_tw, endCName: ele.DailyTrainInfo.EndingStationName.Zh_tw, stopTimes: ele.StopTimes }); }); /* 使用jQuery的$.extend做copy array */ tripArray = $.extend(true, [], tmpArray); apiObject.handleTripSelect(tripArray); }); } } return apiObject; } function handleOnload(assembleObj){ var navItem = document.getElementsByClassName('nav-item'); var contentWrap = document.getElementsByClassName('content-wrap'); var railStation = assembleObj.railStation(); var priceTable = assembleObj.priceTable(); var timeTable = assembleObj.timeTable(); var apiObject = { callApi: function() { $.ajax({ method: 'GET', url: 'https://ptx.transportdata.tw/MOTC/v2/Rail/THSR/ODFare?$format=JSON', dataType: 'text' }) .done(function(data) { priceTable.handleData(data); timeTable.handleData(data); }); }, handleClick: function() { Array.prototype.forEach.call(navItem, function(ele) { ele.addEventListener('click', function() { var dataIndex = this.getAttribute('data-index'); Array.prototype.forEach.call(navItem, function(ele) { removeClass(ele, 'active'); }); Array.prototype.forEach.call(contentWrap, function(ele) { removeClass(ele, 'active'); }); addClass(contentWrap[dataIndex], 'active'); addClass(this, 'active'); }); }); }, execute: function() { railStation.execute(); priceTable.execute(); this.handleClick(); this.callApi(); } } return apiObject; }; var assembleObj = { railStation: railStation, priceTable: priceTable, timeTable: timeTable } var executeOnload = handleOnload(assembleObj); executeOnload.execute(); })(); /* 立即函示 + 閉包 */ ``` ```css= .clearfix:after { display: table; content: ""; clear: both; } body { margin: 0; } p { margin: 0; } .body-title { margin: 0; color: #555354; line-height: 60px; text-align: center; } .nav-wrap { margin-bottom: 30px; background: #585455; color: #FFF; text-align: center; } .nav-item { display: inline-block; padding: 0 15px; cursor: pointer; line-height: 40px; user-select: none; transition: all .3s ease; } .nav-item:hover, .nav-item.active { background: #D85221; } /* station */ .station-title, .time-title { margin: 0 auto 20px; color: #D85221; font-size: 24px; font-weight: bold; } .station-item { width: 75px; height: 40px; line-height: 40px; cursor: pointer; text-align: center; float: left; } .station-item:hover, .station-item.active { color: #D85221; font-size: 20px; } .station-item:hover:after, .station-item.active:after { display: block; content: ''; background: #D85221; width: 100%; height: 5px; } .station-content { padding: 25px; background: #F7F7F7; } .station-content-wrap { float: left; } .station-image { float: right; } .station-cname { color: #868686; font-size: 25px; font-weight: bold; } .station-ename { margin-bottom: 15px; color: #868686; font-size: 25px; } /* content-wrap */ .content-wrap { display: none; margin: 0 auto 60px; width: 100%; max-width: 900px; } .content-wrap.active { display: block; } /* price-wrap */ .price-wrap { display: none; } .price-wrap.active { display: block; } .price-wrap#select-table { margin: 0 auto; width: 400px; } .price-title { color: #D85221; text-align: center; font-size: 24px; letter-spacing: 5px; } .price-box { margin-bottom: 20px; text-align: center; } .price-item { display: inline-block; width: 75px; } .price-dolor { border: solid 1px #CCC; height: 50px; line-height: 50px; } .spot-wrap { margin-bottom: 30px; text-align: center; } .spot-content { display: inline-block; margin: 0 20px; } .spot-content p { color: #D85221; font-size: 20px; } .spot-content select { width: 150px; height: 35px; font-size: 20px; } .select-journey { margin-bottom: 15px; color: #D85221; font-size: 24px; font-weight: bold; } .select-price-item { padding: 0 20px; height: 40px; line-height: 40px; color: #494949; font-size: 20px; } .select-price-item:nth-child(2n+1) { background: #F7F7F7; } .select-price-item:nth-child(2n) { background: #DCDCDC; } /* type-wrap */ .type-wrap { margin-bottom: 30px; text-align: center; } .type-button { display: inline-block; padding: 5px; cursor: pointer; user-select: none; font-weight: bold; letter-spacing: 1px; } .type-button:hover, .type-button.active { color: #87C252; } /* time-table */ .time-table { width: 100%; } .time-table-title { color: #D85221; } .time-table-head { background: #585455; text-align: center; } .time-table-head .item-table { color: #FFF; } .time-table-body { text-align: center; } .time-table-body>div:nth-child(2n+1) { background: #F7F7F7; } .time-table-body>div:nth-child(2n) { background: #DCDCDC; } .time-table-body .item-table:first-child { color: #D85221; } .item-trip { display: inline-block; padding: 10px 0; width: calc(100% / 12); color: #494949; text-align: center; } .item-table { display: inline-block; width: 33.33%; height: 40px; font-size: 18px; line-height: 40px; } /* query-wrap */ .query-wrap { padding: 20px; background: #F7F7F7; } .query-title { margin-bottom: 15px; font-size: 20px; color: #585455; } .query-button { background: #6F6F6F; height: 40px; cursor: pointer; user-select: none; color: #FFF; line-height: 40px; text-align: center; } .query-rule-wrap { margin-bottom: 20px; } .query-date { padding: 5px; width: 150px; height: 40px; } .query-rule { padding: 5px; width: 150px; height: 40px; opacity: 0; visibility: hidden; transition: all .5s ease; } .query-rule.show { opacity: 1; visibility: visible; } /* time-table-wrap */ .price-tr { padding: 0 20px; height: 40px; line-height: 40px; color: #494949; font-size: 20px; } .price-tr:nth-child(2n+1) { background: #F7F7F7; } .price-tr:nth-child(2n) { background: #DCDCDC; } ```