Detekce stavu zařízení pomocí CSS Media Queries a JavaScriptu

Schopnost detekovat stav zařízení v kteroukoli chvíli je důležitá z mnoha důvodů, a proto je důležité, aby CSS a JavaScript webové aplikace byly vzájemně synchronizované. Při práci na redesignu Mozilla Developer Networks jsem zjistil, že naše četné dotazy na média, i když byly užitečné, někdy zanechaly JavaScript v nevědomosti o stavu zařízení. Prohlíží si uživatel web na obrazovce počítače, tabletu nebo telefonu? Jednoduché z pohledu CSS, ale CSS přímo nekomunikuje s JavaScriptem. Vytvořil jsem systém založený na dotazech na média a z-index který mi může sdělit, v jakém mediálním dotazu si uživatel stránky v kteroukoli chvíli prohlíží, takže mohu provádět úpravy dynamických funkcí, kdykoli budu chtít!

CSS

Prvním krokem je vytvoření mediálních dotazů důležitých pro vaši aplikaci a cíle. Pro příklad vytvoříme tři nové dotazy na média (nezahrnující výchozí „vše“), které budou obsahovat čtyři stavy:desktop (výchozí, nevyžaduje dotaz na média), „malá plocha“, tablet, a telefon. Pro každý z těchto stavů nastavíme jiný z-index na prvku, který použijeme k detekci stavu. Prvek bude umístěn mimo obrazovku, takže nebude vidět; pamatujte, že jediným účelem je držet z-index hodnotu, kterou můžeme načíst pomocí JavaScriptu:

/* default state */
.state-indicator {
    position: absolute;
    top: -999em;
    left: -999em;

    z-index: 1;
}

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

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

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

Každé z těchto čísel z-indexu bude v našem kódu JavaScript indikovat, že jsme v dané době v dané velikosti zařízení. Nepokoušíme se odhalit, že uživatel dává dané zařízení, protože uživatel může mít jednoduše okno plochy v úzkém stavu, ale poskytuje nám informace o ploše obrazovky kvůli rozvržení naší webové aplikace.

JavaScript

Pravděpodobně budete chtít znát velikost obrazovky na DomContentLoaded ale protože se na to můžete chtít kdykoli zeptat (protože uživatel může změnit velikost svého okna), budeme vyžadovat, aby byla funkce zavolána, aby získala stav, kdykoli bude požadován:

// Create the state-indicator element
var indicator = document.createElement('div');
indicator.className = 'state-indicator';
document.body.appendChild(indicator);

// Create a method which returns device state
function getDeviceState() {
    return parseInt(window.getComputedStyle(indicator).getPropertyValue('z-index'), 10);
}

Řekněme tedy, že chcete tento systém použít k určení, zda se má widget zpočátku zobrazit nebo skrýt:

if(getDeviceState() < 3) { // If desktop or small desktop
    // Show the widget....
}

Někdo by mohl namítnout, že spoléhání se na tyto číselné klávesy může být matoucí nebo obtížně udržovatelné, takže byste k tomu mohli použít objekt:

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';
}

V tomto případě byste mohli vytvořit více podmínek pro angličtinu:

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

Možná lepší možností je použití obsahu pseudoprvků s CSS a JavaScriptem:

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

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

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

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

Tento klíč lze poté získat pomocí tohoto JavaScriptu:

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

Je také na vás, jak tento kód uspořádáte. Pokud máte jeden globální objekt, kam připínáte metody a vlastnosti (jako window.config nebo window.app globální nebo podobné), můžete na to připnout metodu. Dávám přednost použití modulů formátu AMD, ale každému svému. Můžete jej přidat jako plugin do jQuery nebo kteroukoli knihovnu JavaScript, kterou používáte. Bez ohledu na to, jak implementujete, nyní máte spolehlivou a snadno použitelnou detekci stavu zařízení na straně klienta díky mediálním dotazům!

Další úsilí

Víme, že ke změně velikosti obrazovky dochází, ať už jde o ruční změnu velikosti okna na ploše nebo prostřednictvím změny orientace na mobilních zařízeních, takže můžeme chtít, aby nějaký typ systému událostí oznámil tyto změny, když k nim dojde. Je to tak jednoduché, jak byste očekávali:

var lastDeviceState = getDeviceState();
window.addEventListener('resize', debounce(function() {
    var state = getDeviceState();
    if(state != lastDeviceState) {
        // Save the new state as current
        lastDeviceState = state;

        // Announce the state change, either by a custom DOM event or via JS pub/sub
        // Since I'm in love with pub/sub, I'll assume we have a pub/sub lib available
        publish('/device-state/change', [state]);
    }
}, 20));

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

    }
});

Všimněte si, že jsem použil funkci debouncing k omezení rychlosti, kterou resize metoda je spuštěna - to je neuvěřitelně důležité pro výkon. Zda použijete události typu pub/sub nebo vlastní DOM, je na vás, ale jde o to, že vytvoření posluchače změny stavu je snadné!

Miluji tento systém správy velikosti a stavu zařízení. Někteří budou poukazovat na matchMedia jako možnost, ale problém s tím je potřeba mít dotazy na média jak v CSS, tak v JavaScriptu, a protože dotazy na média mohou být složité, zdá se to spíše jako noční můra údržby než pouhé použití z-index kódy. Lidé by se mohli hádat, než by se dalo použít window.innerWidth měření, ale to se jednoduše pokouší převést dotazy na média na podmínky JS a to je také noční můra. Na tom je také hezké, že můžete použít stejný typ systému pro jakýkoli typ znaku dotazu na média, jako je kontrola portrait nebo landscape orientace.

V každém případě to vyzkoušejte a dejte mi vědět, co si myslíte!