巧妙使用CSS媒體查詢(Media Queries)和JavaScript判斷瀏覽器設備類型的好方法

有無數的理由要求我們在任何時候都應該知道用戶是使用的什麼設備瀏覽我們的網站——寬屏,普通屏,平板,手機?知道這些特徵,我們web應用的CSS和JavaScript才能同步做相應的操作。在給Mozilla Developer Networks改版設計的過程中,我發現使用CSS媒體查詢(media queries)雖然非常的有效,但有時,JavaScript卻不能及時知道用戶瀏覽設備的狀態。瀏覽網站的用戶使用的是桌面電腦,還是平板,還是手機?這對於CSS來說很容易,但CSS卻無法將這些信息告訴JavaScript。我發明了一種基於CSS媒體查詢和z-index屬性的方法,能告訴JavaScript用戶當前使用的是什麼屏幕,這樣我能調整JavaScript的動作來響應這種屏幕尺寸。

CSS代碼

首先最重要的還是CSS媒體查詢代碼。這裏只是示例,我們創建了三個媒體查詢規則(但不包括缺省的“all”),它能控制四種屏幕情況:桌面(這是缺省狀態,不需要媒體查詢規則),“小屏幕”,平板,手機。針對每一種屏幕,我們給它一種不同的z-index值,這個值我們可以用JavaScript檢測到。我們把這個元素定位到屏幕外,這樣它就不會顯示出來;記住,它的作用就是存放z-index值,我們要用javaScript獲取這個值。

/* 缺省屏幕 */
.state-indicator {
    position: absolute;
    top: -999em;
    left: -999em;

    z-index: 1;
}

/* 小屏幕 */
@media all and (max-width: 1200px) {
    .state-indicator {
        z-index: 2;
    }
}

/* 平板 */
@media all and (max-width: 1024px) {
    .state-indicator {
        z-index: 3;
    }
}

/* 手機 */
@media all and (max-width: 768px) {
    .state-indicator {
        z-index: 4;
    }
}

每種z-index都在告訴我們的JavaScript當前的用戶使用的是什麼規格的屏幕。我們並不像知道用戶究竟使用的是什麼設備,因爲你可以將瀏覽器寬度拉的很窄,但我們需要就是可視寬度,這樣我們可以調整應用的佈局。

JavaScript代碼

也許你認爲可以在DomContentLoaded時知道屏幕的大小,但我們需要實時知道屏幕的大小(因爲用戶會調整瀏覽器窗口的大小),我們需要有個方法獲取當前窗口的屬性:

// 創建狀態指示元素
var indicator = document.createElement('div');
indicator.className = 'state-indicator';
document.body.appendChild(indicator);

// 獲取設備類別的方法
function getDeviceState() {
    return parseInt(window.getComputedStyle(indicator).getPropertyValue('z-index'), 10);
}

使用這個方法,你就能檢測出頁面佈局/js飾件中那些需要顯示,那些需要隱藏。

if(getDeviceState() < 3) { // 如果是桌面電腦後小屏幕電腦
    // 顯示js飾件....
}

有人也許會認爲這些數字太容易搞錯,讓代碼很難維護。其實我們可以用一個對象來處理這種事情:

function getDeviceState() {
    var index = parseInt(window.getComputedStyle(indicator).getPropertyValue('z-index'), 10);

    var states = {
        2: 'small-desktop',
        3: 'tablet',
        4: 'phone'
    };

    return states[index] || 'desktop';
}

這樣,你就一個寫出更具可讀性的邏輯判斷:

if(getDeviceState() == 'tablet') {
    // Do whatever
}

也許使用CSS的僞元素的content屬性是個更好的方法:

.state-indicator {
    position: absolute;
    top: -999em;
    left: -999em;
}
.state-indicator:before { content: 'desktop'; }

/* 小屏幕桌面 */
@media all and (max-width: 1200px) {
    .state-indicator:before { content: 'small-desktop'; }
}

/* 平板 */
@media all and (max-width: 1024px) {
    .state-indicator:before { content: 'tablet'; }
}

/* 手機 */
@media all and (max-width: 768px) {
    .state-indicator:before { content: 'mobile'; }
}

用下面的JavaScript方法獲取關鍵的內容:

var state = window.getComputedStyle(
    document.querySelector('.state-indicator'), ':before'
).getPropertyValue('content')

如何組織這些代碼全看你自己了。如果你有一個全局對象,例如window.configwindow.app等,你可以把這些方法放到裏面。我傾向於模塊化這些功能,你可以把它做成jQuery插件或JavaScript工具包。不管你如何實現,它們都是你可以信賴的、簡單易用的檢測用戶設備的好方法。

更上一層樓

我們知道屏幕尺寸會發生變化——用戶手工調整瀏覽器大型或手機用戶調整了手機方向,所以,當這些事件發生時,我們需要讓系統告訴我們。下面這段簡單的代碼估計是你需要的:

var lastDeviceState = getDeviceState();
window.addEventListener('resize', debounce(function() {
    var state = getDeviceState();
    if(state != lastDeviceState) {
        // 保持當前的狀態
        lastDeviceState = state;

        // 宣告狀態變化,通過自定義的DOM事件或JS 消息發佈/訂閱模式,
        // 我喜歡後者,所有就假設使用了一個這樣的工具庫
        publish('/device-state/change', [state]);
    }
}, 20));

// 用法
subscribe('/device-state/change', function(state) {
    if(state == 3) { // or "tablet", if you used the object

    }
});

注意,這裏我使用了debounce方法來執行resize事件發生時的動作——這對於性能來說非常重要。是使用自定義DOM事件還是使用發佈/訂閱模式,你自由選擇,因爲都很簡單。

我覺得這種方法非常的好。有人可能會指出使用matchMedia也可以有相同效果,但問題是你需要在CSS裏和JavaScript裏都使用媒體查詢,而有些媒體查詢語句可能會很複雜,甚至會成爲你的噩夢,不如使用簡單的z-index。也有人會說可以使用 window.innerWidth來判斷,但這樣JS裏獲取的屬性和CSS裏的媒體查詢就需要相互轉換了,同樣也會成爲你的惡魔。我的方法的好處就在於你可以用它判斷其他類型的媒體查詢屬性,例如檢查手機是橫向(landscape)還是豎向(portrait)。

不管怎樣,你可以試一下,告訴我你的感覺!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章