Велосипед с электроприводом вместо рейсового автобуса. Хочу поделиться своим методом адаптации скриптов jQuery (сам привык писать скрипты на jQuery, но разницы нет, все описанное верно и для чистого javascript) параллельно адаптации веб-верстки.

Для адаптации под различные разрешения экранов и различные устройства используют медиа-запросы в CSS. Допустим у нас заложена адаптация нашей верстки под 2 диапазона: от 0 до 479 пикселей и до 800 пикселей.

@media screen and (max-width: 800px) {
  /* CSS */
}
/* И */
@media screen and (max-width: 479px) {
  /* CSS */
}

Мы хотим использовать различную анимацию и/или запросы к серверу в зависимости от размера экрана. Т.е. нам необходимо параллельно срабатыванию меди-запроса из CSS реагировать в скриптах javascript. Для этого существует, как минимум, 3 способа.

Академический. Специальный метод javascript: window.matchMedia, определяющий срабатывание медиа-запроса, аналогичного CSS:

var mql = window.matchMedia('all and (max-width: 479px)');
if (mql.matches) {
    // размер окна 479px или меньше
} else {
    // нет, размер окна более 479px 
}

Минусом данного метода считаю наличие явного указания диапазона разрешения экрана в 2-х местах: CSS и javascript. При изменении диапазона в CSS можно забыть поменять его в javascript, в результате мы получим баг в работе сайта, при определенной ширине окна верстка адаптируется, а соответствующая ей функциональность или анимация еще не подключится. Плюс существует ограничение поддержки данного метода в устаревших версиях браузеров.

Метод костыля №1. Самый простой и, наверное, очевидный метод — добавить в скрипт условие проверки текущего размера окна браузера.

К минусом данного метода относится также наличие дублирования размеров экрана в CSS и в javascript и некоторые проблемы с получением адекватных кроссбраузерных размеров окна на различных платформах.

Метод костыля №2. Теперь о методе, которым пользуюсь я. Заключается он в использовании html-флагов для каждого диапазона. В качестве флага может выступать реальная конструкция верстки, появляющаяся только в нужном нам диапазоне размеров, либо пустой однопиксельный div.
Html:

<!DOCTYPE html>
<html>
	<head>
		<title>Тест флагов</title> 
	</head>
	<body>
		<div class=”max_width_800”></div>
		<div class=”max_width_479”></div>
	</body>
</html>

CSS:

.max_width_800{
	display: none;
}
.max_width_479{
	display: none;
}

@media screen and (max-width: 800px) {
	.max_width_800{
		display: block;
	}
	.max_width_479{
		display: none;
	}
}

@media screen and (max-width: 479px) {
	.max_width_800{
		display: none;
	}
	.max_width_479{
		display: block;
	}
}

А в самом скрипте мы просто проверяем показан ли наш маркер:

if ($('.max_width_800').is(':visible')) {
    // код jQuery
}

if ($('.max_width_479').is(':visible')) {
    // код jQuery
}

В данном примере мы явно указываем диапазоны размеров экрана только в CSS и при изменении нам не нужно беспокоиться о работоспособности скриптов. Также на кроссбраузерность последнего примера влияет только поддержка браузером медиазапросов.

Идем дальше и подключаем мобильный скрипт только при условии необходимости:

var head = $('head');
includeScripts(head);
$(window).resize(function(){
	includeScripts(head);
});

function includeScripts(head){

	if ($('.max_width_800').is(':visible')) {
		head.append('<script type="text/javascript" src="mobile-800.js" id=”script-mobile-800”></script>');
		$('#script-mobile-479').remove();
	}

	if ($('.max_width_479').is(':visible')) {
		head.append('<script type="text/javascript" src="mobile-479.js" id=”script-mobile-479”></script>');
		$('#script-mobile-800').remove();
	}

}

Спасибо за внимание!
Поделиться с друзьями
-->

Комментарии (14)


  1. BuccapuoH
    04.01.2017 17:00
    +2

    Если я правильно понял, то вы чтобы не писать ширину экрана в двух местах (CSS, JS), решили писать ее в трёх (CSS, JS, HTML).

    И даже если отбросить идентификаторы c явным указанием ширины, всеравно нужно следить за тремя местами. Это при том, что использование разных идентификаторов сделает код сложным для понимания людьми, которые его не писали.

    Подход рабочий, я не спорю, но ради одного IE9 (Caniuse CSS Media Queries и Caniuse window.matchMedia) изобретать такой велосипед — ИМХО не стоит.


    1. Lda
      04.01.2017 17:01

      Да, вы правы, задействовано теперь 3 области, вместо 2-х, но целью было избавиться от явных идентификаторов (как вы правильно заметили), мне так удобнее.


  1. k12th
    04.01.2017 17:29
    +2

    Непонятно, какой вообще use case: нормальные мобильные браузеры все вроде поддерживают matchMedia, а IE9 там нет. Если даже почему-то это нужно в IE9, то есть же полифилл.


    .is(':visible')

    Сурово. Насколько я помню, производительность у этого поделия совершенно чудовищная.


  1. Kenya
    05.01.2017 15:14

    Отказаться от явных идентификаторов в js и добавить явные идентификаторы в html — выгода все же сомнительная чутка


    1. Lda
      05.01.2017 15:16

      Вы не правильно поняли, явное указание диапазонов в моем примере есть только в одном месте — CSS, маркеры мы добавляем 1 раз, 1 раз пишем код скриптов и изменяя в будущем диапазон размера экрана не возвоащаемся ни в html ни в скрипты, все и так будет работать.


      1. justboris
        06.01.2017 00:21

        чтобы это было понятнее, можно классы назвать max_width_mobile, max_width_tablet, а не max_width_479 и max_width_800


        1. Lda
          06.01.2017 00:55

          Можно, но я пытался сохранить связь с изначальными медиа-запросами, для сквозного понимания.


        1. Lda
          06.01.2017 01:02

          P.s. Нет никакой разницы для чего используются медиа-запросы, платформа может не меняться.


  1. Mr_Boombastic
    06.01.2017 01:04

    А что, если сделать, например, вот так:

    CSS (Stylus, Rupture):

    body:after
    	display block
    	+above('s')
    		content 's'
    	+above('m')
    		content 'm'
    	+above('l')
    		content 'l'
    

    JS (ES6, JQuery):
    $(window).on('resize', () => {
    	console.log(window.getComputedStyle(document.body, ':after').getPropertyValue('content'));
    });
    

    Таким образом мы можем передать в js названия переменных, к которым привязан наш адаптив в css.


    1. Lda
      06.01.2017 01:07

      Смысл сильно не меняется, как я и указал в статье, в роли маркера может выступать реальная конструкция верстки.


    1. sashamorozov
      06.01.2017 01:26

      Пока создавал своё небольшое решение подобной проблемы вы меня опередили :)

      Но вот мой рабочий вариант: JSFiddle

      Плюс, что не надо плодить и контролировать элементы в DOM, и с учетом mobile first вообще никаких проблем нет с определением min-width или max-width.


      1. Lda
        06.01.2017 01:28

        Да, можно так, я намеренно упростил все для описания сути приема.


      1. Mr_Boombastic
        06.01.2017 01:42
        +1

        И все-таки, узкое место в таком подходе — производительность .getComputedStyle()
        Раз уж речь о переменных, то наверняка и какая-никакая система сборки имеется. В таком случае, имхо, самый роскошный вариант — подхватывать хэш, в котором хранятся диапазоны, и передавать его в тот-же webpack, например.


  1. sashabeep
    06.01.2017 14:19
    +1

    Тоже внезапно понадобилась похожая проверка изнутри скриптов, сейчас я часто использую Foundation, и там есть утилита для определения текущего значения вьюпорта, но каждый раз ее дергать расточительно, поэтому я клею класс к html

    $(document).foundation();
    
    function setBodyQuery(){
    	var viewport = "size-" + Foundation.MediaQuery.current;
    	$('html').removeClass("size-xxlarge size-xlarge size-large size-medium size-small");
    	$('html').addClass(viewport)
    }
    
    //pass css viewport class on resize
    $(window).on('changed.zf.mediaquery', function(event, newSize, oldSize) {
    	setTimeout(setBodyQuery(), 50)
    });
    
    
    $(document).ready(function () {
    	
    	//pass css viewport class
    	setBodyQuery();
    
    //EODR	
    });
    

    И дальше в любом месте можно проверить, какой класс у html