Статьи

Полноэкранные веб-приложения

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

Последующие изменения в методах и программном обеспечении

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

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

Например, рассмотрим следующий снимок экрана мобильного сайта:

Веб-сайт с браузером Chrome

Приведенный выше снимок экрана был сделан на iPhone 4 с отображенной адресной строкой Mobile Safari и панелью инструментов.

Теперь взгляните на тот же скриншот без интерфейса браузера:

Веб-сайт без браузера Chrome

Версия сайта для iPhone получила 60 пикселей за счет удаления адресной строки вверху и 44 пикселей за счет удаления панели кнопок снизу для общего увеличения 104 логических пикселей вертикального экранного пространства (количество места, получаемого на устройствах Android, варьируется , но результат аналогичный). Когда вы пытаетесь создать захватывающий опыт, по скриншотам выше легко понять, какую большую разницу может принести такое небольшое изменение.

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

Если ваше веб-приложение ориентировано только на iOS , то идеальным решением будет установить следующий метатег в части <head> вашего HTML-документа:

1
<meta name=»apple-mobile-web-app-capable» content=»yes» />

Это полностью удалит как адресную строку браузера, так и панель инструментов из Mobile Safari, как показано на втором снимке экрана выше.

В дополнение к тому факту, что этот код будет надежно работать только на устройствах iOS , есть еще одна серьезная проблема с этим подходом: он будет работать только после того, как пользователь добавил веб-сайт на домашний экран, и когда пользователь запускает сайт независимо от Мобильное Сафари .

Добавление веб-приложения на домашний экран

Я читал неподтвержденные сообщения о том, что метатег подход на самом деле будет работать и на некоторых устройствах Android, но, конечно, он не работает на моем Nexus S и, похоже, официально не поддерживается Android вообще.

Это явно не идеально. Добавление веб-сайтов на домашний экран iOS является чем-то вроде неясной функции iOS, которую многие пользователи даже не подозревают, что это возможно и вряд ли будет использоваться при случайном просмотре веб-страниц.

Возможно, однажды поставщики браузеров объединят и предоставят один кросс-платформенный метатег для детального управления пользовательским интерфейсом браузера, не препятствуя нормальному потоку приложений веб-браузера ( какова была бы жизнь, если бы это действительно произошло ). До тех пор нам придется взять все в свои руки старым добрым способом: с помощью JavaScript.

Контрапункт: разрешение разработчикам контролировать наличие адресной строки и / или панели вкладок предоставляет творческую свободу разработчикам за счет свободы конечного пользователя и общего опыта просмотра. Без единого шаблона UX для перехода назад или ввода нового URL пользователи будут запутаны при просмотре и в некоторых случаях не смогут покинуть сайт без полной перезагрузки браузера.

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

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

Для тех из вас, кто просто хочет код без повествования:

function hideAddressBar()
{
if(!window.location.hash)
{
if(document.height < window.outerHeight)
{
document.body.style.height = (window.outerHeight + 50) + ‘px’;
}
setTimeout( function(){ window.scrollTo(0, 1); }, 50 );
}
}
window.addEventListener(«load», function(){ if(!window.pageYOffset){ hideAddressBar(); } } );
window.addEventListener(«orientationchange», hideAddressBar );

Я размещаю приведенный выше код на GitHub: Gist, так что не стесняйтесь размещать, изменять или предлагать изменения. Просто помните, что в лучшем случае это зависит от браузера. Это может измениться в будущем. Это может не охватывать каждый крайний случай. Не тестируется на Blackberry и Windows Phone 7.

ОБНОВЛЕНИЕ 3/3/2011:
Благодаря отклику Джона Боксалла, приведенному ниже, я добавил еще одно условие в обработчик событий load. Функция hideAddressBar() теперь будет вызываться только в том случае, если пользователь не начал прокручиваться до запуска события load.

Для тех из вас, кто хочет узнать, как и почему работает этот аккуратный трюк, читайте дальше!

По сути, трюк сводится к тому, что можно сжать в одну строку JavaScript:

1
window.scrollTo(0, 1);

scrollTo является методом объекта браузера window со следующей подписью:

1
scrollTo(x, y);

Первый аргумент контролирует расстояние для прокрутки окна по оси X, а второй аргумент контролирует расстояние для прокрутки окна по оси Y.

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

Итак, зачем перемещать только ось Y на 1 пиксель? Разве это не должно быть 60 пикселей для iPhone? Это была моя первоначальная мысль. Тем не менее, адресная строка технически не является частью области просмотра документа. Вместо того, чтобы прокручивать содержимое на 60 пикселей, мы на самом деле пользуемся особенностью WebKit (ошибка?), scrollTo автоматически удаляет адресную строку при scrollTo метода scrollTo . В моем тестировании я смог добиться желаемого эффекта на iOS, установив значение Y в любое целое число, включая -10, 0, 1 или 60. Однако на Android только положительные целые числа достигли желаемого эффекта, следовательно, сделав «1 msgstr «лучшее смещение Y для использования в браузере.

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

Добавление слушателя события:

1
window.addEventListener(«load», function() { window.scrollTo(0, 1); });

Добавление встроенного прослушивателя событий:

1
<body onload=»window.scrollTo(0, 1);»>

Во встроенном теге script (для тех, кто чувствует себя мятежным):

1
2
3
4
5
    <script>
        window.scrollTo(0, 1);
    </script>
</body>
</html>

Если вы попробуете все три примера на Android, все должно работать без сбоев (хотя третий пример особенно уродлив). Однако, если вы попытаетесь сделать это на iOS, ничего не произойдет.

По причинам, которые мне не совсем понятны, Mobile Safari на iOS не способен применить хак с прокруткой только с одним из вышеупомянутых слушателей событий.

Для того, чтобы это работало на iOS, вам нужно создать небольшую задержку между тем, когда срабатывает прослушиватель событий, и когда scrollTo метод scrollTo .

Это можно легко сделать с помощью метода setTimeout как показано:

1
2
3
4
window.addEventListener(«load», function()
{
    setTimeout( function(){ window.scrollTo(0, 1); }, 100 );
}

Подпись метода для функции setTimeout :

1
setTimeout(code, milliseconds, [ lang ])

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

На этом этапе наш фрагмент кода, скрывающий адресную строку, должен выглядеть примерно так:

Слушатель событий:

1
2
3
4
5
<head>
    <title>Fullscreen Test</title>
    <script>
      window.addEventListener(«load», setTimeout( function(){ window.scrollTo(0, 1) }, 0));
    </script>

Встроенный слушатель события:

1
<body onload=» setTimeout( function(){ window.scrollTo(0, 1) }, 0); «>

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

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

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

1
<meta name=»viewport» content=»width=device-width, initial-scale=1.0″>

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

Второй подход заключается в использовании простого атрибута CSS. Вы можете применить достаточно большое значение min-height либо к тегу body либо к любому другому элементу уровня блока на своей странице, чтобы учесть пустое пустое пространство. Однако здесь необходимо соблюдать осторожность по двум причинам: точное значение пикселя, необходимое для атрибута min-height будет варьироваться в зависимости от initial-scale (т. Е. Масштаба) страницы, и значение будет меняться, если пользователь поворачивается от портрета к ландшафтный режим или наоборот. Основной синтаксис для установки атрибута min-height в теге body показан ниже:

1
body { min-height: 900px;

Опять же: фактическое значение пикселя зависит от начального масштаба / масштаба вашего сайта. Возможно, вам придется идти довольно высоко или довольно низко.

Третий подход состоит в том, чтобы динамически проверять свойство document.height отношению к свойству window.outerHeight а затем динамически увеличивать размер document.height при необходимости.

Следующий фрагмент кода JavaScript является некаркасным решением этой проблемы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<script>
   window.addEventListener(«load», function(){
       if(document.height <= window.outerHeight)
       {
           document.body.style.height = (window.outerHeight + 50) + ‘px’;
           setTimeout( function(){ window.scrollTo(0, 1); }, 50 );
       }
       else
       {
           setTimeout( function(){ window.scrollTo(0, 1); }, 0 );
       }
   }
   );
 </script>

В строках 5 выше я добавил, казалось бы, произвольное количество отступов (+50). Это было необходимо для того, чтобы эффект работал на iOS и Android. Мне также пришлось изменить положение вызова setTimeout поскольку iOS не будет производить автопрокрутку сразу после установки document.body.style.height . Что мне показалось особенно странным, так это то, что мне нужно было не только изменить положение вызова setTimeout , но и для iOS мне пришлось добавить, казалось бы, произвольную задержку +50, если я только что изменил высоту документа. Первоначально это было не так (при использовании прослушивателя load без установки нового значения высоты документа).

Вариации вышеупомянутого взлома браузера уже широко реализованы в сети. Однако существует по крайней мере один вариант использования, при котором принудительная прокрутка браузера до 0,1 является совершенно неправильным подходом: посетители, заходящие на ваш сайт по якорной (или внутренней) ссылке. Чтобы учесть этот scrollTo(0, 1) случай, вам нужно вызывать scrollTo(0, 1) если хеш-тег отсутствует в URL. Чтобы реализовать этот подход, все, что мы должны сделать, это проверить наличие значения в window.location.hash а затем обернуть наш прослушиватель события load в это условие. Это оставляет нам что-то вроде следующего:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
if( !window.location.hash )
{
    window.addEventListener(«load», function(){
        if(document.height <= window.outerHeight + 10)
        {
            document.body.style.height = (window.outerHeight + 50) +’px’;
            setTimeout( function(){ window.scrollTo(0, 1); }, 50 );
        }
        else
        {
            setTimeout( function(){ window.scrollTo(0, 1); }, 0 );
        }
    }
    );
}

Другая проблема, с которой вы можете столкнуться, связана с изменением ориентации устройства. На iOS, когда пользователь поворачивает телефон из портретного режима в ландшафтный режим, смещение прокрутки не будет изменено автоматически (кажется, что Android не страдает от этой проблемы). Это означает, что ваш пользователь останется где-то дальше вниз по странице, чем предполагалось.

Исправление для этого состоит в том, чтобы установить прослушиватель событий в window.onorientationchange для уведомления об изменении ориентации, а затем снова выполнить вызов window.scrollTo(0, 1) после изменения.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
function hideAddressBar()
{
    if(!window.location.hash)
    {
        if(document.height <= window.outerHeight + 10)
        {
            document.body.style.height = (window.outerHeight + 50) +’px’;
            setTimeout( function(){ window.scrollTo(0, 1); }, 50 );
        }
        else
        {
            setTimeout( function(){ window.scrollTo(0, 1); }, 0 );
        }
    }
}
 
window.addEventListener(«load», hideAddressBar );
window.addEventListener(«orientationchange», hideAddressBar );

Представленное выше решение, кажется, отлично работает для меня как на Android, так и на iOS, но есть еще одна проблема, которая может относиться или не относиться к вашему проекту: что если пользователь значительно прокрутил страницу вниз до изменения ориентации устройства? В этом случае сброс отображения на 0, 1 приведет к тому, что пользователь потеряет свое место в документе. Учет этого в значительной степени зависит от реализации, но суть заключается в том, чтобы просто установить порог оси Y, а затем сбросить смещение прокрутки до 0, 1, если пользователь еще не прокрутил этот порог.

Некоторые платформы, такие как SenchaTouch, фактически блокируют адресную строку за пределами экрана, предотвращая прокрутку пользователя за пределы заданного порогового значения по оси Y. Это, конечно, возможно, но я не буду обсуждать, как это сделать, поскольку считаю, что это решение представляет собой серьезную проблему с юзабилити, особенно на Android. Однако, если вы настроены на достижение этого эффекта, вам, вероятно, придется поэкспериментировать с атрибутом window.pageYOffset .

Насколько мне известно, в настоящее время не существует решения для удаления панели инструментов / панели кнопок на iOS из нижней части Mobile Safari только с помощью JavaScript. Единственный способ достижения этого эффекта, о котором я знаю, — это метатег, описанный в начале этого урока. Поправьте меня если я ошибаюсь!

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

Взлом браузера, подобный тому, который я описал, скрывает адресную строку, игнорируя лучшие практики. Реализация, которую я объяснил в этом руководстве, была протестирована на Android Nexus S, iPhone 3GS и iPhone 4, но вполне возможно, что я где-то пропустил крайний случай. Я также не совсем уверен, что отображаемая реализация будет продолжать работать как есть в будущем, поэтому я был очень удивлен, обнаружив так много основных веб-фреймворков (например, iUI, jQuery Mobile, SenchaTouch) и видных веб-сайты (например, Gmail, Yahoo, Apple), основанные на некоторых пользовательских вариантах этого хака. Причина, я думаю, проста: лучшего, не поддерживающего JavaScript решения в настоящее время не существует.

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

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

Во-вторых, я хотел развеять магию того, как фреймворки, такие как SenchaTouch или iUI, сделали этот эффект возможным. Когда я первоначально решил использовать SenchaTouch для внештатного проекта некоторое время назад, «магия» фреймворка для создания приложений на экране была одним из основных UX-эффектов, которые мне понравились. Важно понимать, что этот же эффект может быть легко реализован в чистом JS, независимо от того, используете ли вы JavaScript-фреймворк в своем проекте.

Наконец, главная причина, по которой я хотел рассмотреть эту проблему в таких подробностях, — повысить осведомленность о том, насколько непостоянным является этот подход. Несмотря на тот факт, что варианты этого трюка получили широкое распространение, я считаю, что это в лучшем случае неумелый клудж, а в худшем — ненадежный браузер-зависимый хак, который может продолжать работать, а может и не работать в будущем. Я хотел бы призвать тех, кто занимается браузерным бизнесом и сообществом разработчиков веб-приложений и мобильных устройств в целом, добиваться более стандартизированного, независимого от JavaScript подхода для решения этой проблемы UX. Я думаю, что метод мета-тегов, реализованный Apple, является отличным шагом в правильном направлении, но, как уже упоминалось выше, он недостаточно отвечает потребностям сообщества разработчиков.

Реальный вопрос: что вы думаете? Давайте поговорим об этом в разделе комментариев ниже.

Я не сомневаюсь, что некоторые из наших читателей смогут улучшить код, который я представил в этом руководстве. Если вы видите в этом посте что-то, что можно оптимизировать или улучшить, оставьте свой отзыв ниже! Вы также можете связаться со мной через Twitter ( @markhammonds ), хотя мне иногда требуется время, чтобы ответить на твиты или DM. Лучший способ связаться со мной — либо в комментариях ниже, либо с помощью контактной формы на Mobileuts +. Если я приму одно из ваших предложений по улучшению, я обновлю этот пост и процитирую ваше имя или ручку!

Не хочешь поверить мне на слово?

Взгляните на следующие ресурсы, на которые я наткнулся при исследовании этого поста: