Hacking the color picker game — MutationObserver

Color picker game

上週在FB看到有人分享 http://kolor.moro.es/ 這款辨色能力的小遊戲,身為一個 web developer(偽 developer)單然是要來考驗一下自己的「絕對色感」。殊不知,我根本是色盲來著,分數都在四五百徘徊。於是惱羞之下決定讓 JavaScript 同學幫我玩。

 

Game logic

 

遊戲的邏輯很簡單,當中間的色塊顏色改變時,在時間限制內於底下的選項選擇跟它顏色一樣的選項就得分。難的點在於越後面的關卡選項越多,而且選項顏色也越相近,例如:

 

這到底是要選個毛啊

因此常常在找到正確選項前秒數就用光了。

 

JavaScript MutationObserver

為了要直接找出正確的答案,最直覺的想法就是:當題目(上方色塊)改變顏色時,於底下選項找出相對應色碼者。Web APIs 提供了一個非常實用的方法:MutationObserver,作用是對DOM tree 的變動作出反應。用法是:

// 要監聽的對象
var target = document.querySelector('#target_id')

// 當目標發生改變,要執行的handler function
var callback = (mutationsList, observer) => {
    mutationsList.map( mutation => {
        // do something
        // console.log(mutation);
    })
}

// observer instance
var observer = new MutationObserver(callback);
 
// 用於監聽的種類選項
var config = {
    attributes: true,
    childList: true,
    characterData: true
}
 
// 開始監聽
observer.observe(target, config);
// 停止監聽
observer.disconnect();

 

callback function 裡的 mutationsList 為包含目標 element 變動的事件 MutationRecord instance 的 array 。由於 MutationObserver 是以 async 的的方式觸發,代表著如果有一堆快速頻繁的變動出現,只會觸發一次事件,這是為了避免同步觸發可能帶來的效能下降(請參考:How Mutation Observers are different,一次插入2500個element的範例)。

 

MutationRecord

MutationRecord 為描述該次變動的物件,包含下列屬性:

  • type:這筆變動的種類,可以是 attributes:屬性變動、characterData:文字變動或 childList:其子節點之變動
  • target:該變動發生的元素節點,為屬性變化、文字變化或子元素發生變化的那個節點
  • addedNodes:新增的節點,若無新增則為空 NodeList
  • removedNodes:刪除的節點,若無刪除則為空NodeList
  • previousSibling:該節點的前一個 sibling,或 null
  • nextSibling:該節點的後一個 sibling,或 null
  • attributeName:發生變動的屬性名稱,或 null
  • attributeNamespace:發生變動的變動屬性的 namespace,或 null
  • oldValue:對 attributes 來說是變動前的屬性值,對 characterData 來說是變動前的內容,childList 則是 null

 

Config

config 是一個 MutationObserverInit 物件,用來設定想要監聽的事件種類,有以下幾種:

  • childList :目標結點之子節點的變動(新增、刪除、文字變動)
  • attributes :目標節點的屬性變化
  • characterData :內容、文字變化
  • subtree :同時監聽目標元素之所有後代子元素(整串)
  • attributeOldValue :監聽 attributes 時同時紀錄舊的值
  • characterDataOldValue :監聽 characterData 時同時紀錄舊的值
  • attributeFilter :若僅需要監聽某幾種 attributes ,以陣列表示(如:['class']

將想要監聽的事件設為 true 就可以了

 

Wrapping things up

把這些東西組合在一起,達到「當題目顏色改變時,馬上知道哪個選項是正確答案,並在答案上面提示」的結果。由於我們只關心題目區域的顏色變化,因此 config 裡僅將 attributes 設為 true。而且該題目變化時只產生了一個 MutationRecord ,所以直接拿出來判斷。

var targetNode = document.querySelector('#kolor-kolor');
var config = { attributes: true };
var callback = function(mutationsList, observer) {
    if (mutationsList[0].type == 'attributes') {
        console.log('attribute change!');
        let ans = document.querySelector('#kolor-kolor').style.backgroundColor;
        document.querySelectorAll('#kolor-options a').forEach( (e) => {
            if (e.style.backgroundColor == ans) {
                e.text = 'Ans!';
            }
        })
    }
};

var observer = new MutationObserver(callback);
observer.observe(targetNode, config);

另外,由於答題時間也列入分數的考量,因此顯示正確答案選項的地方可以改成 e.click() ,直接在找到正確答案時按下按鈕,節省時間。

 

Result

結果出爐:高達980分!!經過反覆測試,似乎最高只能到980分,應該就是上限了。

不過,好空虛啊・・・・・・

1 則迴響

  1. 通告:现代浏览器观察者 Observer API 指南 | 站点资源

關閉迴響。