JQuery assignment

The Box Fixer (12/2 version)

還記得前幾堂課我們一直用到的box嗎

 

現在,因為來自古老東方的奇幻神秘力量

 

四個box - 文具箱玩具箱工具箱以及嚇人箱

 

就這樣出現在你的房間裡

 

突然,你的耳邊傳來神秘人的聲喚...

 

神秘人:「醒醒阿肥宅,你知道你多久沒整理房間了嗎?」

 

你:「呃...這是演哪齣?」

 

神秘人:「你不需要知道我是誰,你只要知道,現在要是不快點整理妳的房間裡那些充滿神秘力量的box們,等你一入睡,那些box們就會化身成為期末59小精靈纏著你!就問你怕不怕?」

 

你:「我不怕,就算當了一個我,還有千千萬萬個我。」

 

聽見你充滿自信的回答,神秘人的聲音就這樣消失了

 

但,實際上,你還是很在意期末59小精靈

 

於是,你還是決定遵從神秘人的指示

 

打開repl,架設好jquery環境

 

踏上了整理神秘boxes的旅程...

由於每天都盯著電腦

慢慢對整個房間感到陌生了

於是,眼觀四面、耳聽八方,環顧四周的你

感慨了聲:「啊~原來是長這樣子阿」

<!-- Place below code in index.html -->

<div id="room"></div>
/* Place below code in style.css */

* {
  box-sizing: border-box;
}

body {
  background: #222;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  font-family: "微軟正黑體";
}

#room {
  display: flex;
  flex-wrap: wrap;
  width: 600px;
  height: 600px;
  margin: 0 auto;
  padding: 4px;
}

再來,你開始找尋在房間裡散落的神秘箱子

 

並打算以下面的設計圖將箱子聚集在一起

 

才好方便整理

但由於箱子是神祕的

 

其既存在又不存在

 

其真實的存在(骨架)得靠自己來塑照

<!-- Place below structure in index.html -->

<div id="box-template" class="box">
  <h2 class="title">A box</h2>
  <ul class="items"></ul>
</div>

神秘箱子可能是藍色圓形的,也可能是綠色方形的

 

無論如何,讓自己來決定樣式總是一定行

 

/* Place below code in style.css file */

.box {
  position: relative;
  width: calc(50% - 6px);
  height: calc(50% - 6px);
  margin: 3px;
  background: #9ED9EB;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 20px rgba(0, 0, 0, 0.1) inset;
  border-radius: 5px;
}

.box:before {
  position: absolute;
  content: "";
  box-shadow: 0 10px 25px 20px #518C96;
  top: 40px;
  left: 14px;
  bottom: 50px;
  width: 15%;
  z-index: -1;
  transform: rotate(-8deg);
}

.box:after {
  position: absolute;
  content: "";
  box-shadow: 0 10px 25px 20px #518C96;
  top: 40px;
  right: 14px;
  left: auto;
  bottom: 50px;
  width: 15%;
  z-index: -1;
  transform: rotate(8deg);
}

.box .title {
  text-align: center;
}
.box .items {
  width: 85%;
  height: 120px;
  margin: 0 auto;
  padding-top: 8px;
  border: 2px dashed #F7EEEE;
  border-radius: 5px;
}

神秘箱子感應到其即將有形體可以依附

 

於是,boxes的資訊傳入了你的腦中

// Place below code in script.js 

var boxes = [
  {
    name: "文具箱",
    items: [
      { name: "百樂原子筆", owner: "文具箱" },
      { name: "死亡筆記本", owner: "文具箱" },
      { name: "尖叫雞", owner: "玩具箱" }
    ]
  },
  {
    name: "玩具箱",
    items: [
      { name: "泰迪熊", owner: "玩具箱" },
      { name: "一打L夾", owner: "文具箱" },
      { name: "放三個月的百香果", owner: "嚇人箱" }
    ]
  },
  {
    name: "工具箱",
    items: [
      { name: "羅賴把", owner: "工具箱" },
      { name: "熱熔槍", owner: "工具箱" },
      { name: "UNO", owner: "玩具箱" }
    ]
  },
  {
    name: "嚇人箱",
    items: [
      { name: "你沒有女朋友", owner: "嚇人箱" },
      { name: "萬用工具人", owner: "工具箱" },
      { name: "熱騰騰的紅字成績單", owner: "嚇人箱" }
    ]
  }
];

萬事起頭難

你開始試著形體化第一個神秘箱子 - part1

 

// Place below code in script.js 

// 以下所有跟DOM有關的變數皆會以dom_開頭以識別用

// 先從畫面下手,使用一個id為box-template的box來複製箱子,要記得刪除複製出來的id,因為id是唯一的
var dom_box = $("#box-template").clone();
dom_box.removeAttr("id");

// 在JS陣列中取得第一個box的資訊,並以下面步驟,試著將其形體化
var box = boxes[0];

/*
   使用 $(selector).find(child_selector) 找到box名稱欄位,
   find是traversal操作,回傳的也是一個jquery element,也就是 $(selector),
   再使用 $(selector).text(some_text) 填入box名稱
*/
var dom_box_title= dom_box.find("<名稱欄位selector>");
dom_box_title.text("<箱子名稱>");

試著形體化第一個神秘箱子 - part2

 

// Place below code in script.js 

// 再來對箱子內物品做處理,要把東西放進箱子裡
var dom_box_items = dom_box.find("<物品空間selector>");

// 先取得箱子中的物品陣列
var box_items = box.items;
  
// 使用for-loop,將資料中的物品們一一轉換成html element,再將其放入箱子裡
for (var j = 0; j < box_items.length; j++) {
  var item = box_items[j];
  var temp_item = "<li class=\"item\">" + item.name + "</li>"; // *註1*
  dom_box_items.append(temp_item);
}
  
/*
  最後,請把第一個box放入房間,
  $(selector).append()可以接受html element字串,或是jquery element
*/
$("#room").append("<第一個箱子>");


/*
  註1: 一般html element,其屬性value會以一對雙引號包起來,<div class="xxx"></div>
       在程式中若外層有字串處理,且已有使用雙引號,則內部雙引號必須採用 → \",
       或是直接使用單引號 → ',
       e.g. "<li class=\"item\">" or "<li class='item'>"
*/

試著形體化第一個神秘箱子 - part3

/* Place below code in style.css */

/* 由於第一個箱子出現了,作為template的box已經沒有出現的必要,故將其隱藏 */

#box-template {
  display: none;
}

試著形體化所有神秘箱子

 

 

// Place below code in script.js 

// 這次,我們要一次解決四個箱子,所以需要改寫一下
for (var i = 0; i < boxes.length; i++) {
  // 前幾步驟的code
}

發現for中有for了嗎?

這叫做nested loop,也就是「巢狀迴圈」

別擔心,這邊我們僅僅使用基本款

只要內外層loop的參數不要相撞即可

(外層用i,內層就用j,反之亦然)

可是為什麼要巢狀迴圈呢?

 

請語意上思考一下~

確認每項物品是不是放在對的箱子

對的標綠、錯的標紅

// Place below code in script.js 

for (var j = 0; j < box_items.length; j++) {
  var item = box_items[j];
  var temp_item = "<li class=\"item\">" + item.name + "</li>";

  var itemClass = ???; // 請使用getItemClass函式來取得判斷class

  dom_box_items.append(temp_item);
  /*
    在物品被插入到html後,才能使用jquery去取得該element,
    經由getItemClass可以得到物品是否放對箱子的判斷class,
    使用 $(selector).addClass(some_class) 來為item增加class
  */
  var dom_item = dom_box_items.find(".item:last-child");
  $(dom_item).addClass(itemClass);
}

// Don't move this function
function getItemClass(box_name, item_box_name) {
  return box_name === item_box_name ? "correct" : "incorrect";
}
/* Place below code in style.css */

.correct {
  color: green;
}

.incorrect {
  color: red;
}

更進一步,讓我們從箱子名稱就能辨識箱子是否整理好

// Place below code in script.js

/*
  先假設箱子是整理好的,在items的for-loop中,
  只要任一item沒放對箱子,那這箱子便不是整理好的
*/

var box_is_correct = true; // 宣告一Boolean
for (var j = 0; j < box_items.length; j++) {
  /*
    ...
  */
  var itemClass = getItemClass(box.name, item.owner);
  if (itemClass == "incorrect") {
    box_is_correct = false;
  }
  /*
    ...
  */
}

/*
  請使用if-else,判斷 box_is_correct 是否正確時,分別為箱子名稱增加正確的樣式,
  一樣錯的標紅、錯的標綠;
  此外,針對未整理好的箱子,其名稱再使用 $(selector).css("property", "value");
  將其font-style設為italic(斜體)
*/

if (箱子整理好) {
  dom_box_title.addClass("<正確的class名稱>");
} else {
  dom_box_title.addClass("<不正確的class名稱>");
  dom_box_title.css("自己試");
}

看來樣式部分都搞定了,來整理箱子吧

// Place below code in script.js

// 注意到每個箱子中都有不屬於其箱子的物品
// 請宣告一函式叫做 shuffle ,其中使用for-loop對每個箱子做處理

function shuffle() {
  // 這邊一樣是nested loop,試試看吧
  for (...) {
    var box = boxes[i];
    var box_items = box.items;
    var correctItems = []; // 宣告一個新的items,概念上請類比把正確物品先放在地板上
    
    for (...) {
      var box_item = box_items[j];
      if (box.name == box_item.owner) {
        // 請把正確的先放到地板上
        correctItems...
      } else {
        // 錯的則使用array的find方法找到對應該物品的箱子(跟jquery的find不一樣,別搞混了)
        var which_box = boxes.find(function(box) { return box.name == item.owner; });
        // 然後直接放進該箱子
        which_box...
      }
    }

    // 把地板上、正確的物品放回去
    box.items = correctItems;
  });

  console.log("整理過後: ", boxes); // 來確認結果是否正確
}

// 雖然解出來了,但解法好像有點兒彆扭?試著用別的想法來解!!

在畫面上加個進行「整理」的button

// Place below code in script.js

// 沒有名稱的function,稱作匿名函式,因為其名稱並不重要,我們只需要它執行裡面的工作
$("#btn-shuffle").click(function () { shuffle(); });
/* Place below code in style.css */

#btn-shuffle {
  position: fixed;
  left: calc(50% + 1.5px);
  top: calc(50% + 9px);
  transform: translate3d(-50%, -50%, 0);
  z-index: 2;
  width: 80px;
  height: 80px;
  border: 1px solid #e1e1e1;
  border-radius: 50%;
  font-size: 16px;
  cursor: pointer;
  background: aliceblue;
}
<!-- Place below structure in index.html -->

<body>
  <button id="btn-shuffle">整理</button>
  <!-- 其餘不動 -->
</body>

嘿!等等,畫面上並沒有改變阿

沒錯,除了改變boxes陣列

我們還需要更新DOM - part1

// Place below code in script.js

// 宣告一函式 reRender

function reRender() {
  // 基本上長得跟最前面幾step很像,但這次我們只需要修改值,不需要重新append箱子到room裡
  for (var i = 0; i < boxes.length; i++) {
    var dom_box = $(`.box:nth-child(${i + 1})`);
    var dom_box_title = dom_box.find(".title")
    var box = boxes[i];

    var dom_box_items = dom_box.find(".items");
    dom_box_items.empty(); // 使用 $(selector).empty() 先清空原本的items,下方再append正確的回去

    var box_items = box.items;
    var box_is_correct = true;
    for (var j = 0; j < box_items.length; j++) {
      var item = box_items[j];
      var temp_item = "<li class=\"item\">" + item.name + "</li>";
      var itemClass = getItemClass(box.name, item.owner);
      if (itemClass == "incorrect") {
        box_is_correct = false;
      }
      dom_box_items.append(temp_item);
      var dom_item = dom_box_items.find(".item:last-child");
      $(dom_item).addClass(itemClass);
    }
    if (!box_is_correct) {
      dom_box_title.addClass("incorrect");
      dom_box_title.css("font-style", "italic");
    } else {
      dom_box_title.addClass("correct");
    }
  }
}

更新DOM - part2

 

好像哪裡不對?為什麼箱子名稱樣式還是錯的QQ?

// Place below code in script.js

// 宣告一函式 reRender

function reRender() {
  for (var i = 0; i < boxes.length; i++) {
    // ...
    for (var j = 0; j < box_items.length; j++) {
      // ...
    }
    
    /*
      由於重新被append的只有items,標題還是原樣,
      也就是說一開始使用 $(selector).addClass(some_class) 的狀態會保留,
      所以我們需要先使用 $(selector).removeClass(some_class) 來移除其原本class,
      然後才進行 $(selector).addClass(some_class)。
      $(selector).css() 亦是同理,正確的箱子名稱無須斜體,請把font-style改回normal
    */

    if (!box_is_correct) {
      dom_box_title.removeClass("???").addClass("incorrect");
      dom_box_title.css("font-style", "italic");
    } else {
      dom_box_title.removeClass("???").addClass("correct");
      dom_box_title.css("font-style", "???");
    }
  }
}

Congratulations

 

請分享你的成果吧~~

jq_assignment

By David Tsui

jq_assignment

  • 303