初試啼聲,只用原生 JS 跟 CSS 寫「口罩地圖 」Ep.05


Posted by ABow_Chen on 2020-07-12

寫在前頭

這個系列總算來到尾聲,今天我們會把口罩地圖的剩下 JavaScript 的部分講完,主要功能部分就完成了,剩下的就是漫長的 RWD 調整過程,有時間的話也許再開一篇純粹講解 CSS 及一些 UI 設計的想法,但這算是番外篇了,如果等不及的朋友,也可以直接參考我的 GitHub 觀看 CSS 的部分,接下來就讓我們一起迎接本系列的倒數第 2 篇~(灑花)

本篇記錄,會講到搭配 select 的改變,把相對應的藥局清單,渲染在畫面列表中,及點選列表中的藥局,即會定位到地圖上該藥局的位置,並展開 Popup。

讓藥局列表渲染在左側 panel 下半部

首先我們先看完整的程式碼:

function renderList(town,county){
    let str = '';
    for(let i = 0;i<data.length;i++){
        const countyName = data[i].properties.county;
        const townName = data[i].properties.town;
        const pharmacyName = data[i].properties.name;
        const maskAdult = data[i].properties.mask_adult;
        const maskChild = data[i].properties.mask_child;
        const pharmacyAddress = data[i].properties.address;
        const pharmacyPhone = data[i].properties.phone;
        const pharmacyNote = data[i].properties.note;
        let maskAdultJudge;
        let maskChildJudge;

        if (maskAdult >= 100) {
            maskAdultJudge = 'bg-sufficient';
        } else if (maskAdult < 100 && maskAdult !== 0) {
            maskAdultJudge = 'bg-insufficient';
        } else {
            maskAdultJudge = 'bg-none';
        }
        if (maskChild >= 100) {
            maskChildJudge = 'bg-sufficient';
        } else if (maskChild < 100 && maskChild !== 0) {
            maskChildJudge = 'bg-insufficient';

        } else {
            maskChildJudge = 'bg-none';
        }
        if(countyName == county && townName == town){
            str+=`<ul class="maskContent">
            <div class="pharmacyTitle" data-lat="${data[i].geometry.coordinates[1]}" data-lng="${data[i].geometry.coordinates[0]}">
            <li data-name="${pharmacyName}"><span>${pharmacyName}</span></li>
            <p class="infoText"><i class="fas fa-map-marker-alt"></i> ${pharmacyAddress}</p>
            <p class="infoText"><i class="fas fa-phone-square-alt"></i> ${pharmacyPhone}</p>
            <p class="noteText"> ${pharmacyNote}</p>
            <div class="panelMaskNum" data-name="${pharmacyName}">
            <div class="${maskAdultJudge}">
            <div class="infoLayout">
            <img class="adultIcon" src="" alt="">
            <p>${maskAdult}</p>
            </div>
            </div>
            &nbsp;<div class="${maskChildJudge}">
            <div class="infoLayout">
            <img class="kidIcon" src="" alt="">
            <p>${maskChild}</p>
            </div>
            </div>
            </div>
            </div>
            </ul>`
        }
    }
        document.querySelector('.pharmacyList').innerHTML = str;
        var pharmacyTitle = document.querySelectorAll('.pharmacyTitle'); 
        var pharmacyNameList = document.querySelectorAll('.maskContent'); 
        clickPharmacyGeo(pharmacyTitle, pharmacyNameList);
}

這些內容在之前也有寫過一次,非常相似的,接著我們分段來看:

// 先宣告 str 為空字串 ,待會取得資料後,會使用 str 組字串渲染到畫面上
let str = '';
// 使用 for 迴圈取出 data 裡的資料
for(let i = 0;i<data.length;i++){
    // 宣告變數,並將資料賦值到變數上
    const countyName = data[i].properties.county;
    const townName = data[i].properties.town;
    const pharmacyName = data[i].properties.name;
    const maskAdult = data[i].properties.mask_adult;
    const maskChild = data[i].properties.mask_child;
    const pharmacyAddress = data[i].properties.address;
    const pharmacyPhone = data[i].properties.phone;
    const pharmacyNote = data[i].properties.note;
    let maskAdultJudge;
    let maskChildJudge;
    // 使用判斷式,判斷口罩數量,回傳相對應的樣式設定
    if (maskAdult >= 100) {
        maskAdultJudge = 'bg-sufficient';
    } else if (maskAdult < 100 && maskAdult !== 0) {
        maskAdultJudge = 'bg-insufficient';
    } else {
        maskAdultJudge = 'bg-none';
    }
    if (maskChild >= 100) {
        maskChildJudge = 'bg-sufficient';
    } else if (maskChild < 100 && maskChild !== 0) {
        maskChildJudge = 'bg-insufficient';

    } else {
        maskChildJudge = 'bg-none';
    }
    // 接著再用判斷式來篩選資料,如果縣市名相同且鄉鎮區名也相同,那就把資料累加到 str
    if(countyName == county && townName == town){
        // 字串累加方式使用 ES6 的樣板字面值,可以更直覺的組出 HTML 結構
        str+=`<ul class="maskContent">
        <div class="pharmacyTitle" data-lat="${data[i].geometry.coordinates[1]}" data-lng="${data[i].geometry.coordinates[0]}">
        <li data-name="${pharmacyName}"><span>${pharmacyName}</span></li>
        <p class="infoText"><i class="fas fa-map-marker-alt"></i> ${pharmacyAddress}</p>
        <p class="infoText"><i class="fas fa-phone-square-alt"></i> ${pharmacyPhone}</p>
        <p class="noteText"> ${pharmacyNote}</p>
        <div class="panelMaskNum" data-name="${pharmacyName}">
        <div class="${maskAdultJudge}">
        <div class="infoLayout">
        <img class="adultIcon" src="" alt="">
        <p>${maskAdult}</p>
        </div>
        </div>
        &nbsp;<div class="${maskChildJudge}">
        <div class="infoLayout">
        <img class="kidIcon" src="" alt="">
        <p>${maskChild}</p>
        </div>
        </div>
        </div>
        </div>
        </ul>`
    }
}

接著看這一段程式碼的最後一部分:

// 最後將累加的字串,用 innerHTML 塞進 panel 下半部的 .pharmacyList
document.querySelector('.pharmacyList').innerHTML = str;
// 宣告變數,來選取 DOM 元素,這裡由於有許多個同名的元素,所以要使用 querySelectorAll
var pharmacyTitle = document.querySelectorAll('.pharmacyTitle'); 
var pharmacyNameList = document.querySelectorAll('.maskContent');
// 然後帶入我們要講的最後一個函式
clickPharmacyGeo(pharmacyTitle, pharmacyNameList);

此時畫面雖然有些怪異,但其實已經可以正常運作:

只要選取縣市及鄉鎮區,就可以正確顯示了。

接著,我們在 JavaScript 再加上這一段:

function getData(){
    const xhr = new XMLHttpRequest;
    xhr.open('get','https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json',true)
    xhr.send(null);
    xhr.onload = function(){
        data = JSON.parse(xhr.responseText).features;
        addMarker();
        // 增加預設載入的鄉鎮區及縣市,就不會有上面列表空白的情況產生了
        renderList('永康區','臺南市');
        addCountyList();
    }
}

成果如下:

點選列表中的藥局,地圖會定位至該藥局

接下來是最後一個函式:

function clickPharmacyGeo(pharmacyTitle, pharmacyNameList){
    // 使用 for 迴圈取出 pharmacyNameList 裡的資料
    for(let i=0;i<pharmacyNameList.length;i++){
        // 對每一個 pharmacyTitle 進行監聽
        pharmacyTitle[i].addEventListener('click',function(e){
            //宣告變數,並把經緯度賦值到變數上
            Lat = Number(e.currentTarget.dataset.lat);
            Lng = Number(e.currentTarget.dataset.lng);
            // map 取得經緯度,並設定 zoom in 數值為 20,這會關係到 marker 的 Popup  
            能不能自動展開,所以請依自己的需求設定 zoom in 的數值
            map.setView([Lat, Lng], 20);
            // 對 markers 這個 group 使用 eachLayer 的方法,取得每一個layer
           (marker)
            markers.eachLayer(function (layer) {
                // 宣告變數,並使用 getLatLng 的方法,取得 layer(marker) 的經緯
                度,並賦值到變數上
                const layerLatLng = layer.getLatLng();
                    // 用判斷式比對經緯度,如果 layer(marker)的經緯度與點擊目標的經
                    緯度相同,則展開 Popup
                    if (layerLatLng.lat == Lat && layerLatLng.lng == Lng) {
                        layer.openPopup();
                    }
               });
        })
    }
}

至此,我們已完成口罩地圖的基本功能了!最後,我們來稍微美化一下藥局的列表。

初步美化藥局列表

我們先在 HTML 的 <head> 裡引用 Font Awesome:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.0-12/css/all.min.css"/>

接著,我們在 CSS 裡加上這一段:

.pharmacyList{
    height: 50%;
    overflow:auto;
}

.pharmacyTitle{
    width:90%;
}

.pharmacyList li{
    font-size: 22px;
    font-weight: 900;
    color:#072e00;
    border-left:solid 3.5px #072e00;
}

.pharmacyList li span{
    padding-left: 8px;
}

.pharmacyList ul{
    padding:20px 0 20px 0;
    display: flex;
    justify-content: center;
    border-bottom: solid 0.5px #072e00;
}

.infoText{
    color: #072e00;
    padding-top: 15px;

}

.noteText{
    color: black;
    padding-top: 12px;
    font-size: 12px;
    word-break: normal;
}

.maskContent:hover{
    background:#dfdfdf;
    cursor: pointer;
}

.panelMaskNum{
    padding: 20px 0 10px 0; 
    font-size: 20px;
    display:flex;
    justify-content: center;
}

.infoLayout{
display: flex;
justify-content: center;
align-items: center;
}

初步的美化,就完成了,成果如下:

資料補充


#口罩地圖 #六角學院 #台灣口罩地圖-2020 防疫要贏 #javascript #css #JavaScript 入門篇 - 學徒的試煉







Related Posts

Vue.js 學習旅程Mile 7 – 資料屬性綁定篇:v-bind

Vue.js 學習旅程Mile 7 – 資料屬性綁定篇:v-bind

JS 的浮點數精準度問題 & 十進位小數轉二進位小數

JS 的浮點數精準度問題 & 十進位小數轉二進位小數

❖讀心❖打造「正確」學習 mindset

❖讀心❖打造「正確」學習 mindset


Comments