JavaScript 練習 - 串接環保署空氣品質開放資料

Demo

JavaScript 練習 - 取得環保署開放數據資料 - Demo

Introduction 介紹

串接環保署的空氣數據開放資料,這個項目將達到以下目標

  • 運用 fecth 取得遠端資料
  • 載入資料完成後,關閉 loading ring
  • 將自定義色彩搭配遠端數據

功能

首先設定 HTML 結構,並且設定相應變數:

1
2
3
4
5
6
7
8
ar loading = document.querySelector('.loading')
var select = document.getElementById('location'); //區域選單
var placeTitle = document.querySelector('.location'); //區域名稱顯示
var degree = document.querySelector('.degree');//呈現空氣品質的色彩表
var time = document.querySelector('.time');//更新時間
var infoList = document.querySelector('.infoList');//該區域的詳細鄉鎮列表與數據
var detailTitle = document.querySelector('.detailTitle');//點選鄉鎮後會被呈顯於左方的個別鄉鎮名稱
var detail = document.querySelectorAll('.detail .number');//個別鄉鎮的數據

使用 fetch 取得遠端資料。

這次練習使用 fetch 取得遠端資料。網路說明 fetchjQuery.ajax() 有幾個主要的的差異:

- fetch 會使用 ES6 的 Promise 作回應
- then 作為下一步
- catch 作為錯誤回應 (404, 500…)
- 回傳的為 ReadableStream 物件,需要使用不同資料類型使用對應方法,才能正確取得資料物件。
`fetch` 後方會接 `then()`,這是 `Promise`的特性,資料取得後可在 then 裡面接收。return response.json(); 的資料則會傳到下一個 then();Fetch API 的 Response 物件中的 body 屬性提供了一個 ReadableStream 的實體,這個階段我們無法直接讀取資料內容,而 ReadableStream 物件中,可用 json() 取得資料

將取得的資料儲存在變數,並添加到資料結構中

將遠端資料儲存在變數,並且用 for 迴圈以及 add()方法將縣市的欄位添加到 Set 結構當中。再依序為資料添加新的 DOM 元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var url =
"https://cors-anywhere.herokuapp.com/http://opendata.epa.gov.tw/webapi/Data/REWIQA/?format=json";
fetch(url, {})
.then(response => {
//會得到一個 ReadableStream 的物件
return response.json();
//透過 json()轉成可用的資訊
})
.then(data => {
getData(data); //save data
updateAll("新北市"); //更新所有頁面 ( datail, infoList)
})
.then(() => {
loading.style.display = "none"; //待資料渲染至頁面後,關閉 loading page
})
.catch(err => {
console.log(err);
});

var datalist;

function getData(data) {
datalist = data;
let location = new Set();
for (let i = 0; i < datalist.length; i++) {
location.add(datalist[i].County);
}
addInSelect(location);
}

function addInSelect(location) {
location.forEach(element => {
let option = document.createElement("option");
option.setAttribute("value", element);
option.innerHTML = element;
select.appendChild(option);
});
}

設定監聽事件

設定 2 個監聽事件:獲取該區域全部資料、獲取個別鄉鎮的數據。點擊選單後,會將該縣市的資料都顯示在畫面上,另外,如果點擊下方的單一鄉鎮,如果點到的節點是連結,就會把細節更新在左方

1
2
3
4
5
6
7
8
9
10
11
12
select.addEventListener("change", function(e) {
e.preventDefault();
updateAll(e.target.value);
});
infoList.addEventListener("click", function(e) {
e.preventDefault();
if (e.target.nodeName == "A") {
updateDetail(e.target.textContent);
} else {
return;
}
});

在陣列裡儲存色彩表

色彩資訊儲存在陣列裡面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//儲存色彩資訊

var colors = [
{
status: "良好",
color: "#95F084"
},
{
status: "普通",
color: "#FFE695"
},
{
status: "對敏感族群不健康",
color: "#FFAF6A"
},
{
status: "對所有族群不健康",
color: "#FF5757"
},
{
status: "非常不健康",
color: "#9777FF"
},
{
status: "危害",
color: "#AD1774"
}
];

//套用顏色

for (let i = 0; i < degree.children.length; i++) {
degree.children[i].style.backgroundColor = colors[i].color;
}

顯示選擇地區的資料

更新選擇的縣市所有資料在畫面上,點擊的縣市名稱會顯示在畫面上,並且用那個縣市的第一筆資料的更新時間。這裡要用幾個判斷式:過濾的資料要等於我們點擊的縣市、呈現在列表中的空氣品質顏色是根據取得回來的數據比對剛剛我們自行設定的顏色物件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function updateAll(location) {
//更新標題
placeTitle.innerHTML = location;
//更新時間
time.innerHTML = datalist[0].PublishTime + " 更新";
//清空 infoList
infoList.innerHTML = "";
//更新 infoList

//我們想要呈現在銀幕上的資料,在datalist裡一個一個過去
let data = datalist.filter(item => {
//如果過濾出來的,是我們點選的,就回傳
if (item.County == location) {
return item;
}
});
//以 AQI 由大到小排序

//這裡的data 是上面過濾過的data
let sortedData = data.sort((a, b) => {
let x = a.AQI;
let y = b.AQI;
return y - x;
});

//排序過的資料,要組字串放入畫面
//傳入並在給定的函式當做參數,都執行一次
sortedData.forEach(el => {
//宣告一個新變數,是新的li 節點
let newList = document.createElement("li");
//如果傳入的參數的 AQI 是空值或hypen,就替代為N/A
if ((el.AQI == "") | (el.AQI == "-")) {
el.AQI = "N/A";
}
//上色,在上面的顏色陣列裡尋找,如果顏色的狀態跟傳入的el狀態一樣,回傳顏色
let colorList = colors.find(col => {
if (col.status == el.Status) {
return col;
}
});
//若資料內沒有 status (設備維修) 則套用此顏色
if (colorList == undefined) {
colorList = {
color: "#EEEEEE"
};
}
//組字串並更新
let str = `<div class="infoBox">
<a href='#' class="place infoBox">${el.SiteName}</a>
<div class="AQI infoBox" style="background-color:${colorList.color}">${el.AQI}</div>
</div>`;
newList.innerHTML = str;

infoList.appendChild(newList);
});
//並且更新細項,把排序第一個
updateDetail(sortedData[0].SiteName);
}

更新單一鄉鎮的資料在左邊欄位

更新單一鄉鎮的資料在左邊欄位。點擊單一鄉鎮的名稱,比對資料裡的 SiteName 如果符合,就渲染在左邊欄位。首先要將該鄉鎮的資料重新儲存在一個空陣列裡面細節標題的第一項,更新成上面的 SiteName ,就是該站點的 AQI 屬性。著色的方式同上,它是在 colors 陣列裡面比對要取回的顏色,find 方法會對每個元素執行一次 callback 函式,直到找到一個讓 callback 函式回傳 true 的元素。當元素被找到的時候,find 會立刻回傳該元素,否則 find 會回傳 undefined。這個陣列裡面比對的狀態,當成 col 參數在這個方法裡運作,比對傳入元素的狀態,如果符合,就回傳色彩。所以要判斷這個 colorlist 的 status 是否等於傳入的項目的 status,如果是,就回傳。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function updateDetail(place) {
datalist.find(item => {
//如果在d
if (item.SiteName == place) {
//將各項空污指標存入陣列中
let data = [];
data.push(item.O3, item.PM10, item["PM2.5"], item.CO, item.SO2, item.NO2);
//更新 detailTitle
detailTitle.children[0].innerHTML = place; //更新地點
detailTitle.children[1].innerHTML = item.AQI; //更新分數

//上色
let colorList = colors.find(el => {
if (el.status == item.Status) {
return el;
}
});
detailTitle.children[1].style.backgroundColor = colorList.color;
//將 data內的資料更新至 detail上
for (let i = 0; i < data.length; i++) {
if (data[i] == "") {
data[i] = "N/A";
}
detail[i].innerHTML = data[i];
}
return;
}
});
}

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2020 CYC'S BLOG All Rights Reserved.

UV : | PV :