JS直播班 作業檢討--旅遊套票作業

這是六角學院JS直播班第五周作業的檢討。

作業有現成的 HTML 與 CSS模板,是一個旅遊促銷套票的網站,可以新增套票會同步渲染到畫面上。

我的作業CODE PEN

這也是我第一次抓資料渲染到網頁上,有些笨拙,也有些地方思慮不周,更有些地方根本沒有注意到就犯錯了。

所以我後來又去看了同學的作業,針對自己沒做好的地方改進,以下就是我的檢討:

Level 2 把資料渲染到畫面上

原本我是用字串累加,之後再innerHTML到綁定的DOM元素上:

function renderScene(datas) {
  let info = '';
  datas.forEach(function (item) {
    info += `<li  class="ticketCard"><div class="ticketCard-img">
      <a href="#">
        <img src="${item.imgUrl}" alt="${item.name}">
      </a>
      <div class="ticketCard-region">${item.area}</div>
      <div class="ticketCard-rank">${item.rate}</div>
    </div>
    <div class="ticketCard-content">
      <div>
        <h3>
          <a href="#" class="ticketCard-name">${item.name}</a>
        </h3>
        <p class="ticketCard-description">
        ${item.description}
        </p>
      </div>
      <div class="ticketCard-info">
        <p class="ticketCard-num">
          <span><i class="fas fa-exclamation-circle"></i></span>
          剩下最後 <span id="ticketCard-num"> ${item.group} </span> 組
        </p>
        <p class="ticketCard-price">
          TWD <span id="ticketCard-price">${item.price}</span>
        </p>
      </div>
    </div>
  </li>`

  }
  )
  ticketInfo.innerHTML = info;
}

後來看到同學更好的作法:

  • 把要渲染到畫面上的卡片,單獨做成一個函式,回傳( return ) HTML 碼的字串。
  • 再用陣列 reduce 的方法,把卡片渲染出來。
function cardTemplate(dataObj){
    return `
    <li class="ticketCard">
        <div class="ticketCard-img">
          <a href="#">
            <img src="${dataObj.imgUrl}" alt="圖片">
          </a>
          <div class="ticketCard-region">${dataObj.area}</div>
          <div class="ticketCard-rank">${dataObj.rate}</div>
        </div>
        <div class="ticketCard-content">
          <div>
            <h3>
              <a href="#" class="ticketCard-name">${dataObj.name}</a>
            </h3>
            <p class="ticketCard-description">
              ${dataObj.description}
            </p>
          </div>
          <div class="ticketCard-info">
            <p class="ticketCard-num">
              <span><i class="fas fa-exclamation-circle"></i></span>
              剩下最後 <span id="ticketCard-num"> ${dataObj.group} </span> 組
            </p>
            <p class="ticketCard-price">
              TWD <span id="ticketCard-price">$ ${dataObj.price}</span>
            </p>
          </div>
        </div>
      </li>
    `
}

用陣列的reduce方法累加,把版型組起來。

function render(newData = data) {
  ticketCardArea.innerHTML = newData.reduce(function (accumulator, currentValue) {
    console.log(newData);
    return accumulator + cardTemp(currentValue);
  }, '')
}
  • 初始值設為 ‘’ 空字串,傳入累加器 prev 。
  • 第二個參數則帶入 ticketData 陣列裡的物件,讓它們傳入 cardTemplate 這個函式去累加。

這種方法把版型的資料跟組版的方法分開,我覺得更有邏輯。

版型在網頁一載進來時,data陣列中的資料就要渲染在卡片上,所以要在 JS 程式碼中先呼叫 render 函式。

render();

LEVEL 3 利用表單新增票卡

這部分我的錯誤比較多:

  • 成功新增套票後,所有的輸入欄位可以做清空。
  • 套票組數欄位輸入值新增進去會呈現 undefined,程式碼第 108 行在抓取 HTML 元素時有誤,應該是抓取組數欄位的 input 標籤 id 元素。
  • ticketObj 物件變數缺少了 rate 套票星級屬性。
  • 金額、組數、星級的 type 都需要為 Number,建議可以在程式碼第 124~126 行做轉型。
//新增套票邏輯
//綁定表單輸入框的元素
const ticketName = document.querySelector('#ticketName');
const ticketImgUrl = document.querySelector('#ticketImgUrl');
const ticketRegion = document.querySelector('#ticketRegion');
const ticketPrice = document.querySelector('#ticketPrice');
const ticketNum = document.querySelector('#ticketNum');
const ticketRate = document.querySelector('#ticketRate');
const ticketDescription = document.querySelector('#ticketDescription');
//綁掉輸送按鈕
const addBtn = document.querySelector('.addTicket-btn');
//對按鈕做事件監聽
addBtn.addEventListener('click', function (e) {
  if (ticketName.value === '' || ticketImgUrl.value === '' || ticketRegion.value === '' || ticketDescription.value === '' || ticketDescription.value === '' || ticketNum.value === '' || ticketPrice.value === '') {
    return alert('有些資訊沒有填寫!!!')
  }
  
  if(ticketDescription.value.length > 100){
    return alert('套票描述的字數超過 100 字!!!')
  }
  if(ticketRate.value > 10){
    return alert('套票星級要在 10 (含10) 以下!!!')
  }
  let ticketObj = {};
  ticketObj.id = data.length;
  ticketObj.name = ticketName.value;
  ticketObj.imgUrl = ticketImgUrl.value;
  ticketObj.area = ticketRegion.value;
  ticketObj.description = ticketDescription.value;
  ticketObj.group = Number(ticketNum.value);
  ticketObj.price = Number(ticketPrice.value);
  ticketObj.rate = Number(ticketRate.value);
  data.push(ticketObj);
  data.reverse();
  renderScene(data);
  const form = document.querySelector('.addTicket-form');
  form.reset();
})

為了要捉到表單的資料,我綁定了許多輸入框的DOM元素,結果還少捉了一個,真的是眼花了。

後來發現同學的方法非常聰明,監聽整個 <form> 元素,而不是一個一個的輸入框,然後用陣列的展開運算子(…),把每個輸入框的元素轉為一個一個物件,再用 foreEach() 去取輸入的值,這樣就大功告成了,不用再一一去綁定輸入框的DOM元素來取值。

function addData(e) {
    e.preventDefault();    
    const formArray = [...e.target];
    //展開運算子(...) 會把每個輸入框變成一個物件,捉進陣列
    console.log(formArray);
    //最後一個物件是 submit 按鈕,所以要把它去除掉
    formArray.pop();
  
    const obj = {
      id: ticketData.length,
    };
    //必須去HTML改name的內容,讓它對應到資料的物件
    formArray.map((item) => {
      obj[item['name']] = item.value.trim();
      
    });
    //把數字的字串轉為數字
    obj.group = Number(obj.group);
    obj.price = Number(obj.price);
    obj.rate = Number(obj.rate)
    //表單驗證
    
    if(obj.description.length > 100){
      return alert('套票描述的字數超過 100 字!!!')
    }else if (obj.description === ''){
      alert('套票描述沒有填')
    }
    if(obj.rate > 10){
      return alert('套票星級要在 10 (含10) 以下!!!')
    }
    ticketData.push(obj);
    render();
    regionSearch.value = '';
    formEl.reset();
  }

LEVEL3 城市選單:選擇城市 變換網頁內容

這個部分,我目前的程度寫出來是這樣子:

//選取城市,渲染出符合城市條件的卡片
function selectCity (e) { 
    // ''空字串布林值為 false,利用!反轉變成 true
    if (!e.target.value ) {
      render(ticketData)
      searchResultText.innerHTML = `本次搜尋共 ${ticketData.length} 筆資料`;
    } else {
      let selectedCity = ticketData.filter(function(item){
        return e.target.value === item.area;
      })
      console.log(selectedCity);
      searchResultText.innerHTML = `本次搜尋共 ${selectedCity.length} 筆資料`;
      render(selectedCity);
    }
  }

但是我也有看到同學利用「三元運算子」寫出很簡短的程式碼:

function selectCity(e) {
    const filterStr = e.target.value;
    filterStr === '' ? render() : render(ticketData.filter((item) => item.area === filterStr)) ;
  }

他山之石可以攻錯,如果可以學習別人好的地方,來讓自己進步,就可以讓自己離目標更近一點。

修改過後的作業

參考資料


JS直播班 作業檢討--旅遊套票作業
https://popeye-ux.github.io/2021/11/27/travelticket/
作者
POPEYE
發布於
2021年11月27日
許可協議