Русский
Русский
English
Статистика
Реклама

Адаптивный дизайн

Вырезаем SSR и ускоряем Хабр в 10 раз

06.08.2020 22:09:54 | Автор: admin

Здравствуйте, меня зовут Дмитрий Карловский и я тот ещё токсичный перец. Недавно я источал свои альфа-флюиды на Альфа-банк. Ребята в ответ поступили достойно, и не стали атаковать меня в личку объяснениями, как сильно я не прав, а завели задачу на гитхабе. И даже что-то пофиксили, сделав часть моих претензий несостоятельными. Но не ту часть, где SSR портит всё.


Время утекло, пыль улеглась, и тут история получает продолжение: недавно ко мне обратился продюсер контент-студии Хабра с предложением пропесочить их Торт. Что ж, расчехляем вентилятор!



Сложный случай


Возьмём, например, вот эту страницу, содержащую 2500 комментариев. Это настолько огромная страница, что если вы откроете её в Хроме, то он обрежет её уже на 1400 комментарии. Чтобы прочитать оставшиеся вам придётся открыть её, например, в Огнелисе. Причину этого оставим на совести разработчиков. Давайте лучше подумаем как этого не допустить. Но сперва проведём замеры:


Показатель Десктопная версия (HTML) Мобильная версия (JSON) Ускоренная универсальная версия (JSON)
Размер данных 12 MB 3.4 MB 3.4 MB
Размер сжатых данных 1000 KB 700 KB 700 KB
Время полной загрузки 45 s 42 s 5 s
Время показа первого экрана 5 s 42 s 5 s
Число DOM элементов 116K 100K < 1K
Отзывчивость при прокрутке 700 ms не удалось замерить 30 ms
Пересчёт лейаута 1800 ms не удалось замерить 30 ms
Потребление памяти 15 MB 390 MB 63 MB


Методика измерений


  • Размер данных объём HTML или JSON выдачи. Показывается в девтулзах Хрома, если включить "широкие строки". Вес указан лишь того ресурса, что выдаёт комментарии.
  • Размер сжатых данных то, что девтулзы показывают по умолчанию для загруженных ресурсов.
  • Время полной загрузки время открытия всех комментариев (да, иногда Хром всё же рендерит всё не понятно от чего зависит), обычным секундомером на глазок от нажатия F5 (или ссылки "показать комментарии") до завершения всех видимых пользователю загрузок. Кеш отключался через девтулзы.
  • Время показа первого экрана то же, что и время полной загрузки, но дожидаясь лишь появления контента, заполняющего весь экран, что позволяет начать его читать.
  • Число DOM элементов выводился document.getElementsByTagName('*').length в режиме наблюдения, приведено максимально наблюдаемое число.
  • Отзывчивость при прокрутке длительность блокировки основного потока при скроллинге на одну страницу. Замерялось через профайлер.
  • Пересчёт лейаута длительность блокировки основного потока при однократном изменении размера окна. Замерялось через профайлер.
  • Потребление памяти объём, который показывают девтулзы для основного потока, после полной загрузки и ручного запуска сборки мусора.

Для всех замеров использовался обычный домашний вайфай и ноутбук с развёрнутыми на пол экрана девтулзами. Если что-то не учёл обязательно докопайтесь к этому в комментариях.



Предварительный анализ


У Хабра есть две версии: десктопная и мобильная. Десктопная загружает статью со всеми комментариями единым HTML. Мобильная же поступает хитрее: сначала загружается статья в виде HTML, а по клику на кнопку "комментарии" она подгружает JSON с комментариями и рендерит их с помощью VueJS вместо статьи. Но если на комментарии зайти по прямой ссылке, то будет загружен пререндеренный HTML. Пререндеренный HTML ничем принципиально не отличается от десктопной версии, поэтому я замерял именно вариант с динамическим рендером, который отрабатывает в большинстве реальных сценариев использования.


Как видно, вес JSON примерно в 4 раза меньше HTML. В формате Tree эти данные весили бы ещё меньше, но парсились бы дольше, ибо кастомный парсер на яваскрипте даже более простого формата всё же медленнее нативного JSON парсера. В любом случае, после сжатия, которое применяется сейчас повсеместно, разница уже не столь существенна порядка 30%.


Пререндеренный HTML довольно долго грузится, это связано с двумя факторами:


  1. Параллельно начинают грузиться все изображения со страницы, а это 8 мегабайт несжимаемого трафика, который капитально забивает канал. Решить эту проблему могли бы через loading="lazy", но не стали.
  2. После загрузки очередного куска HTML происходит его подклейка в общее дерево, что вызывает повторное вычисление стилей, лейаута и рендеринга. Так как стоимость этого процесса растёт по мере роста DOM, то зависимость суммарной задержки загрузки от размера HTML тут экспоненциальная. Решается только одним способом уменьшением HTML.

При динамическом рендеринге загрузка данных происходит быстрее, но сэкономленное время компенсируется временем на работу яваскрипта по генерации DOM дерева. В обоих случаях пользователю приходится ждать не меньше полу минуты до возможности прочитать новые комментарии, что крайне долго. Также динамический рендеринг на VueJS не прогрессивен, то есть не позволяет начать чтение контента до его полной обработки.


Кроме того, использование VueJS даёт довольно серьёзное пенальти по памяти оно увеличивается более чем в 25 раз.


Всего на страницу выводится около 100K DOM элементов. Это в среднем около 40 элементов на каждый комментарий.


Браузеру становится очень плохо от такого большого DOM дерева и он начинает серьёзно тупить. Например, обновление экрана при скроллинге, не смотря на его аппаратное ускорение, занимает 700мс. И это чисто скроллинг, без пересчёта лейаута. С ним же почти 2 секунды. А пересчитывается лейаут почти на любое изменение DOM дерева. DevTools тоже порой сходят с ума, что осложняет профилирование.



Выбор стратегии


Основная причина тормозов размер DOM дерева. Поэтому требуется такая реализация, которая позволит иметь на пару порядков меньшее число элементов на странице. Выпишем возможные стратегии:


Сворачивание Паджинация Виртуализация
Описание Ветки комментариев изначально свёрнуты и разворачиваются вручную. На одну страницу выводится ограниченное число комментариев. Чтобы увидеть остальные нужно переходить между страницами Рендерятся только комментарии, попадающие в видимую область, а остальные удаляются.
Решается ли проблема скорости загрузки Да Да Да
Решается ли проблема отзывчивости работы Нет, пользователь с большой вероятностью откроет много веток и получит тормоза. ### Да, размер дерева на каждой странице примерно одинаков и регулируем. Да, размер дерева зависит от размера экрана.
Поведение браузерного поиска по странице Ищет только в развёрнутом. ### Ищет только по текущей странице. ## Ищет только по видимым комментариям. ###
Что может вызвать раздражение пользователя Необходимость постоянно разворачивать ветки комментариев. ## Необходимость переходить между страницами. ## Трудно уследить за иерархией ответов. ### Скачки скроллбара. # Возможные скачки контента при скроллинге. ##
Наибольший уровень недовольства пользователя ### ### ## ### ## ## ### ## #

Выводы:


  1. Виртуализация меньше всего расстроит пользователей.
  2. Так как единовременно отображаются не все комментарии, придётся вручную реализовать поиск по всем комментариям.


Прототипирование


Самый простой способ воспользоваться виртуальным рендерингом переписать страницу на $mol, что позволит нам писать код даже не думая о виртуализации, но всё равно её получить, так как в $mol она встроена под капотом.


Хабр возвращает статью и комментарии в виде JSON. Давайте опишем его схему, чтобы тайпскрипт давал нам подсказки и тайпчек, а рантайм проверял, что выдача сервера соответствует ожидаемой. Для этого мы воспользуемся семейством функций $mol_data:


const Person = Rec({    id: Int,    login: Str,    avatar: Str,})const Comment =  Rec({    id: Int,    author: Maybe( Person ),    children: List( Int ),    message: Str,    timePublished: Maybe( Moment ),})const Comments_response = Rec({    comments: Dict( Comment ),    threads: List( Int ),})const Article = Rec({    titleHtml: Str,    textHtml: Str,})

Некоторые поля опциональны в них возвращается null для сообщений, оставленных НЛО.


Теперь мы можем легко загрузить данные, проверить их и вернуть типизированную структуру. Например, так выглядит загрузка комментариев:


@ $mol_memcomments_data() {    const uri = `https://m.habr.com/kek/v2/articles/${ this.article_id() }/comments/`    const data = Comments_response( this.$.$mol_fetch.json( uri ) )    return data}

Хорошо, данные мы загрузили, осталось их показать. Текст статей и комментариев Хабр возвращает в виде строки, содержащей довольно разношёрстный HTML. Для его отображения воспользуемся компонентом $mol_html_view:


<= Article $mol_html_view    html <= article_content \    highlight <= search    image_uri!node <= image_uri!node \

(Если вы не знакомы с синтаксисом view.tree, предназначенным для декларативной композиции компонент, то можете ознакомиться с кратким или полным его описанием.)


Этот компонент берёт HTML, парсит его и для каждого элемента создаёт соответствующий $mol_view компонент, а он уже сам себя виртуализирует. Кроме того, $mol_html_view позволяет указать строку, которая будет подсвечена в тексте.


Также нам потребовалось перегрузить метод image_uri, который вызывается для каждого IMG элемента, чтобы получить ссылку на картинку. Причина в том, что в атрибуте src в выдаче Хабра изначально находится лишь ссылка на заглушку, а реальная ссылка на изображение берётся из атрибута data-src. Поэтому реализуем этот метод так:


image_uri( node : HTMLImageElement ) {    return node.dataset.src || node.src || 'about:blank'}

Рендеринг дерева комментариев не представляет из себя ничего особенного. Разве что мы добавим возможность сворачивать/разворачивать ветки комментариев.


@ $mol_mem_keycomments_visible( id : number ) : readonly number[] {    if( this.comment_expanded( id ) ) {        return this.comments_all( id )    } else {        return this.comments_filtered( id )    }}

Эта функциональность полезна и сама по себе, но нам она ещё пригодится и для поиска. Дело в том, что $mol пока ещё не умеет прокручивать сролл к заданному компоненту. Штука эта вполне реализуема, но у меня руки пока не дошли, так что если если кто-то возьмётся за это дело было бы супер.


А сейчас, мы, вместо прокрутки страницы между найденными постами, будем сворачивать все ветки комментариев, где искомая подстрока в тексте не встречается. Таким образом пользователь может ввести запрос и просто скроллить страницу. А при желании развернуть ветку и почитать все ответы. Выглядит это примерно так:



Многие пользователи привыкли к хоткею Ctrl+F для поиска по странице, поэтому добавим плагин $mol_hotkey для его перехвата:


plugins /    <= Search_key $mol_hotkey        mod_ctrl true        key * F?event <=> search_focus?event null    <= Theme $mol_theme_auto

Ну а для фокусирования просто доберёмся до нужного нам компонента и скажем ему сфокусироваться:


search_focus( event : Event ) {    this.Search().Suggest().Filter().focused( true )    event.preventDefault()}

Обратите внимание, что мы так же добавили плагин $mol_theme_auto, который автоматически ставит светлую или тёмную тему в зависимости от предпочтений пользователя.


Кроме того, добавим пользователю возможность вручную менять тему, разместив на тулбаре кнопку $mol_lights_toggle:


tools /    <= Lights $mol_lights_toggle    <= Sources $mol_link_source        uri \https://github.com/nin-jin/habrcomment    <= Search $mol_search        query?val <=> search?val \

Заодно мы тут расположили стандартную ссылку на исходники и поисковое поле, связав его двусторонней связью с поисковым запросом.


Наведём немного красоты, переопределив некоторые дефолтные стили, используя модуль $mol_style, который обеспечивает тайпчек и подсказки, учитывающие реальную иерархию компонент:


$mol_style_define( $my_habrcomment , {    Orig: {        flex: {            grow: 1,            shrink: 0,            basis: per(50),        },    },    Article: {        maxWidth: rem(60),    },    Comments: {        flex: {            shrink: 0,            grow: 1,        },    },    Comments_empty: {        padding: rem(1.5),    },} )

Последним штрихом добавим поддержку оффлайна:


include \/mol/offline/install

Этим не хитрым кодом мы установили кеширующий Service Worker, который в случае проблем с соединением будет выдавать данные из кеша.


Не то чтобы оффлайн и переключение цветовых тем были необходимы, но если есть возможность практически бесплатно получить вау фичи, которых нет в оригинале, то почему бы и нет.


Что ж, вот наш прототип читалки хабракомментариев и готов: https://nin-jin.github.io/habrcomment/#article=423889


Держите букмарклет, позволяющий открывать в ней любую тяжёлую статью:


document.location = document.location.href.replace( /\D+/ , 'https://nin-jin.github.io/habrcomment/#article=' )


Анализ прототипа


Код исходников уложился в 400 строк, на написание которых требуется не более пары часов. По функциональности:


  • Отображение произвольной статьи с форматированием
  • Отображение дерева комментариев к статье с форматированием
  • Возможность сворачивать/разворачивать комментарии
  • Поиск по статье/комментариям с подсветкой найденного и сворачиванием лишнего
  • Работа в оффлайне
  • Поддержка тёмной/светлой тем
  • Адаптивность к размеру экрана

Мы добились ускорения полной загрузки огромной страницы в 5 раз. А потребление памяти уменьшилось в 6 раз по сравнению с мобильной версий (и в 2 раза, если отключить виртуализацию). Для мобилок это куда актуальней, чем для десктопа. При этом мы ещё даже не приступили к собственно оптимизации кода.


Если пошаманить можно выиграть ещё несколько секунд. Например, в наивной реализации HTML всех комментариев парсится при открытии страницы для расчёта их минимальных размеров исходя из содержимого. Однако, можно руками задать какое-то константное значение, меньше которого текст комментария точно быть не может:


<= Message $mol_html_view    minimal_height 60    highlight <= search \    html <= message \    image_uri!node <= image_uri!node \

Таким образом парсинг HTML будет происходить лишь при фактическом отображении. Позиция скроллбара, конечно, тогда будет рассчитываться несколько менее точно, но это не страшно.


Итого по скорости загрузки: почти десятикратное ускорение полной загрузки при сохранении времени появления контента в 5 секунд.


А если есть возможность менять API, то можно пойти ещё дальше изначально загружать лишь информацию о вложенности комментариев, а собственно информацию по ним загружать лениво, по мере необходимости. Благодаря виртуализации необходимость эта будет возникать лишь при приближении комментария к видимой области. А самое примечательное, что для перехода на ленивую загрузку даже не придётся обновлять вьюшки достаточно обновить код получения данных. Аналогичным образом легко и переключить клиентский поиск на серверный:


@ $mol_memcomments_data() {    const search = encodeURIComponent( this.search() )    const uri = `https://m.habr.com/kek/v2/articles/${ this.article_id() }/comments/?search=${search}`    const data = Comments_response( this.$.$mol_fetch.json( uri ) )    return data}

Реализована сейчас, разумеется, не вся функциональность. Но всё остальное особой погоды не делает. Чтобы довести до продакшена, надо будет ещё много чего реализовать. Если кто-то готов этим заняться можете попробовать. Правда сперва придётся договориться с руководством Хабра об использовании их API. Я пробовал несколько раз подкатить с разных сторон никакого ответа не получил.


Также можно заметить, что отступы вокруг разных комментариев пляшут как попало это от того, что HTML, отдаваемый Хабром, это какая-то дичь от слова совсем: слова то в параграфах, то в дивах, то параграфов вообще нет, а абзацы разделяются переводами строк или тегом BR, что вообще вырубает виртуализацию. В общем, тут надо либо чтобы Хабр отдавал что-то единообразное и валидное, либо пилить какой-то свой нормализатор выдачи.


$mol_html_view поддерживает сейчас лишь сравнительно небольшой набор HTML-элементов далеко не всё, что может выдавать Хабр. Добавить поддержку остальных в принципе не сложно и она, конечно, будет расширяться по мере необходимости.


Кроме того, есть и технические косяки:


  1. Контент при скроллинге иногда скачет это какой-то косяк в логике виртуализации $mol_list. Не приятно, но жить можно. Как-нибудь конечно же починю.
  2. В сафари автоматически отключается виртуализация сверху, так как он не поддерживает overflow-anchor, необходимый для того, чтобы можно было менять контент сверху от видимой области без скачков видимого контента. Это означает, что страница будет открываться всё так же быстро, но по мере прокрутки вниз будет дорендеривать всё остальное содержимое, пока не отрендерит все 100K элементов, когда пользователь домотает до самого конца.

А вот появление горизонтального скролла на узких экранах и глубоких обсуждениях это не баг, а фича. Лучше уж иногда скроллить в сторону, чем вообще терять понимание какой комментарий на какой отвечает начиная с определённого уровня, как это происходит в оригинале. А вот на широком экране мы не ужимаем все комментарии в одну узкую колонку, а занимаем столько места, чтобы их было комфортно читать.



Резюме


  1. SSR не быстрее грамотной реализации PWA.
  2. Вместо поддержки двух абы как слепленных реализаций для разных девайсов, лучше потратить это время на одну, но толковую и адаптивную.
  3. Выбирая между двух зол, лучше сделать шаг в сторону возможно именно там расположилось добро.
  4. VueJS тупит, а $mol рулит.
  5. Браузеру становится очень плохо, когда в доме много элементалей.
  6. Стоит предпочитать те решения, что не приводят к не контролируемому увеличению числа элементалей в доме.


Ссылки


  1. Исходники читалки можете заметить, что кода там всего ничего.
  2. Страница фрейморка $mol ужаснитесь сколько всего у нас там есть.
  3. Канал с новостями о $mol и MAM подписывайтесь, чтобы быть в курсе всего важного, что с ними происходит.
  4. Канал с видео о $mol когда-нибудь тут появятся видео-туториалы.
Подробнее..

Перевод Адаптивный дизайн как антипаттерн

27.04.2021 16:10:26 | Автор: admin


Анимированная гифка показывает, что происходит на Invidious, когда я сужаю окно браузера на пару пикселей.

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

Уверен, не я один часто сталкиваюсь с подобным. Лично мне нравится, когда окна моего браузера достаточно узкие, но в последние лет десять веб-страницы стали реагировать на такое предпочтение так, будто мне нужна мобильная, функционально урезанная версия страницы. Здесь кроется большая проблема, и не одна.

Пространственный газлайтинг


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

Такая идея пространственной ориентации фундаментальна для проектирования любых пользовательских интерфейсов и для веб-дизайна в частности. Когда пользователь снова и снова посещает ваш сайт, он постепенно разбирается, как здесь все устроено. Чем больше времени вы уделяете дизайну вашего сайта, тем эффективнее пользователи могут с ним управляться, и тем больше им будет нравиться ваш сайт. Задумывались когда-нибудь, почему так много громких скандалов в Вебе связано с редизайном? Вот вам и ответ. Пользователям не нравится, когда усилия, вложенные ими в осваивание сайта, перечеркиваются по прихоти дизайнера, что старый сайт дизайнеру просто наскучил.

Именно такова проблема, которая возникла у меня из-за адаптивного дизайна. Большинство реализаций адаптивного дизайна по сути напоминают редизайн вашего сайта всякий раз, когда пользователь меняет размер окна. Самый вопиющий пример такого рода когда горизонтальная панель меню превращается в вертикальную. В трубу вылетает все то, что пользователь успел зрительно запомнить о расположении ссылок на панели! Думали, эта опция спрятана здесь? Нет же, она там!

Межсайтовая непоследовательность


Но даже если пользователям не нравится адаптивный дизайн, от него особо никуда не денешься. Масса примеров так называемого адаптивного дизайна реализуется при помощи медиа-запросов с применением CSS по сути, это условные операторы, приказывающие браузеру ввести в дело набор правил CSS, если выполняются определенные условия, например, если ширина окна браузера превышает или не превышает заданное количество пикселов. Точное число в данном случае выбирает веб-дизайнер; соответственно, два разных веб-дизайнера почти никогда не выбирают одинаковую ширину.

Думаю, все согласятся, что на настольном ПК мы предпочитаем просматривать большинство веб-страниц в компоновке на всю ширину экрана. Есть исключения, в которых суженная версия страницы оказывается удобнее, но они редки, если не сказать единичны. Как правило, работать с веб-страницей, развернутой на всю ширину, гораздо приятнее и легче.

Чтобы не иметь дела с суженными версиями веб-страниц, пользователю приходится самому регулировать ширину окна браузера, добиваясь, чтобы страница уместилась в окне целиком. Но, поскольку двое веб-дизайнеров почти никогда не укажут одну и ту же ширину в своих медиа-запросах, пользователю невозможно подобрать для окна такую ширину, в которую будут гарантированно укладываться полные версии любых веб-страниц, если только пользователь не сдастся и не удовлетворится полноэкранным режимом либо чрезмерно широким окном. Однако пользователю важно не только иметь доступ к полным версиям веб-страниц, но и удерживать в области видимости сразу множество окон, и именно по этой причине ширину отдельного окна приходится ограничивать.

Казалось бы, почему пользователь просто не отрегулирует размер окна, если попадет на веб-страницу, для просмотра которой требуется окно пошире. В конце концов, именно так приходилось действовать пользователю до появления адаптивного дизайна, когда была возможность просто переключаться между веб-страницами с заданной фиксированной шириной. Но на самом деле не все так просто: на неадаптивных страницах совершенно ясно, нужно ли изменить размер окна, чтобы в нем поместилась вся страница. А на адаптивной веб-странице пользователю ничто не подсказывает, эй! В такое окно, как у тебя сейчас, вся страница не влезет. Нет ни полосы прокрутки, ни обрезки содержимого; в лучшем (или, полагаю, в худшем) случае есть меню-гамбургер или какой-нибудь другой странный как бы мобильный интерфейс.

Что делать пользователю?


Напрашивается мысль, что пользователь мог бы решить эту проблему, отключив поддержку медиа-запросов в браузере. Если бы все было так просто! Корень проблемы в том, что среди веб-дизайнеров распространена практика закреплять за медиа-запросом полноширинный вариант дизайна и по умолчанию выдавать мобильную версию. Отключив медиа-запросы, мы получим на таких страницах результат, ровно противоположный желаемому.

Рациональнее было бы каким-то образом заставить браузер придавать единую задаваемую пользователем ширину таблицам стилей и скриптам на всех веб-страницах. Это желательно хотя бы из соображений приватности.

Идеальное решение предусмотреть возможность выявить медиа-запрос (при наличии такового), который включал бы для сайта полную ширину, и активировать его, при этом отключив все прочие медиа-запросы, связанные с выбором ширины окна. Потенциально такая возможность реализуема как браузерное расширение, при помощи JavaScript, который разбирал бы все медиа-запросы в таблице стилей, выборочно удаляя их по мере необходимости.

Что делать веб-дизайнеру?


Я оставил этот раздел напоследок, поскольку он самый важный. Все вышеизложенное подводит нас к простому выводу:веб-дизайнерам нужно долго и крепко обдумывать, как именно реализовать адаптивный дизайн.

Правда, на более высоком уровне следует задаться вопросом, а в чем вообще смысл адаптивного дизайна. Определенно, такой дизайн очень полезен: современные пользователи выходят в Интернет с самых разных устройств с различной шириной экрана, и эта проблема решается при помощи адаптивного дизайна. Но, возможно, проблема адаптивного дизайна в чрезмерной обобщенности такого решения. Он призван поддерживать работу с любыми мыслимыми размерами экрана, но на самом ли деле экраны столь разнообразны? Скорее, они, напротив, стандартизируются и подразделяются на немногочисленные, легко выделяемые категории. Вот такие:

  1. Настольный ПК/ноутбук
  2. Планшет
  3. Смартфон

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

Поверить не могу, что говорю это, но, может быть, было бы лучше вернуться к обнаружению пользовательского агента, в той или иной форме:

let mobile = navigator.userAgent.match(/Mobi/);let ipad = navigator.userAgent.match(/iPad/);let android = navigator.userAgent.match(/Android/);if (mobile && !ipad)this is a phoneelse if (ipad || android)this is a tablet

Возможно, вышеприведенный код неполон, но, думаю, он покрывает многие базовые вещи. Просто убедитесь, что по умолчанию выдается версия сайта, рассчитанная на ПК, поскольку не все браузеры для ПК единообразно поддерживают JavaScript, а вот все смартфоны и планшеты поддерживают.



Наши серверы можно использовать для разработки и хостинга сайтов.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru