в Разработки

Время загрузки страницы — самая унылая часть веб-серфинга :) До того момента, пока страница не загрузилась до конца, велика вероятность что она будет отображаться не так как мы задумывали — блоки могут «наезжать» друг на друга, текст — не отображаться (пока не загрузились шрифты), про корректную работу скриптов а анимации тоже зачастую можно забыть.

Можно с этим явлением ничего не делать, но корректнее было бы скрыть до момента полной загрузки критичного контента всё содержимое страницы, отобразив вместо него индикатор загрузки. По завершению же загрузки — скрыть индикатор, и показать пользователю уже загруженное и подготовленное браузером содержимое страницы. Именно этим мы сейчас и займемся.

Итак, каким же требованиям должен отвечать наш индикатор загрузки?

  • Отсутствие растровых изображений;
  • Старые браузеры — в топку, мы будем использовать современные CSS3 методы;
  • JavaScript поддерживается и включен по умолчанию более чем на 90% браузеров — не стесняемся его использовать;
  • Минимализм по возможности во всем — это же индикатор загрузки, и именно он должен загрузиться и отрисоваться у клиента быстрее всего;
  • И не забываем про адаптивность под мобильные гаджеты.

Давай посмотрим на довольно стандартную структуру HTML-страницы:

<!doctype html>
<html lang="">
<head>
  <meta charset="utf-8" />
  <!--meta ... /-->
  <!--link rel="stylesheet" type="text/css" href="css/any.css" /-->
  <!--link rel="stylesheet" type="text/css" href="css/your.css" /-->
  <!--link rel="stylesheet" type="text/css" href="css/styles.css" /-->
  <title>...</title>
</head>
<body>

  <!-- +-------------------+ -->
  <!-- | Content goes here | -->
  <!-- +-------------------+ -->
  
  <!--script type="text/javascript" src="js/any.js"></script-->
  <!--script type="text/javascript" src="js/your.js"></script-->
  <!--script type="text/javascript" src="js/scripts.js"></script-->
</body>
</html>

Браузер получает html-код страницы и начинает по порядку загружать все внешние ресурсы в таком порядке, в каком как они указаны в теле страницы. В нашем импровизированном примере первым делом будут загружены и отрендерены 3 CSS-файла (css/any.css, css/your.css и styles.css), после чего — контент страницы, и по его завершению — JS скрипты (js/any.js, js/your.js и js/scripts.js).

Именно в каком порядке они у нас указаны — в таком порядке и будет происходить их загрузка (исключениями являются скрипты с указанием параметра async/defer).

Разрабатываем индикатор загрузки

Для ускорения загрузки страницы код стилей и скрипта индикатора загрузки у нас будет указан прямо в теле html-страницы (inline). Основная его логика будет заключаться в следующем:

  1. Описываем все связанные с ним стили в теге <head> inline-кодом;
  2. Первым делом в теле <body> опишем структуру нашего индикатора загрузки, перед всем содержимым страницы;
  3. После загрузки всех скриптов (а так как они у нас загружаются последними, то считаем что содержимое страницы к этому моменту у нас уже загружено) выполним код скрипта, который проверит статус загрузки документа и скроет индикатор загрузки тем самым показав содержимое страницы.

С точки зрения оформления мы будем использовать подобие прогресс-бара, стилизованного под Mac OS. Так как нам для работы необходим включенный JavaScript не забываем добавить тег <noscript>...</noscript> содержащий напоминание пользователю о его необходимости. Анимация его ни к чему не привязана, сделана просто «для красоты». Внешне результат у нас будет выглядеть так:

page-loading-screenshot

Код верстки:

<div id="page_loading" class="light">
  <div class="loader"><div><div></div></div></div>
  <noscript>
    <div class="nojs">Please enable <strong>JavaScript</strong> and reload this page
      <p><a href="http://goo.gl/d5o4zF" target="_blank" rel="nofollow">How enable Javascript</a></p>
    </div>
  </noscript>
</div>
body.noscroll{
  min-height:100%;
  overflow:hidden
}

#page_loading{
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  opacity: 1;
  z-index: 9999
}

.loader{
  position: absolute;
  top: 50%;
  left: 50%;
  height: 4px;
  width: 380px;
  margin: -2px 0 0 -190px
}

.loader>div{
  animation-duration: 3s;
  animation-name: loader_width;
  background-image: linear-gradient(to right,#4cd964,#5ac8fa,#007aff,#34aadc,#5856d6,#ff2d55);
  background-size: 380px 4px;
  height: 100%;
  position: relative
}

.loader>div>div{
  height: 64px;
  position: absolute;
  top: 100%;
  transform: skew(45deg);
  transform-origin: 0 0;
  width: 100%
}

@keyframes loader_width {
  0%,100%{transition-timing-function:cubic-bezier(1,0,0.65,0.85)} 0%{width:0} 100%{width:100%}
}

@-webkit-keyframes loader_width {
  0%,100%{transition-timing-function:cubic-bezier(1,0,0.65,0.85)} 0%{width:0} 100%{width:100%}
}

#page_loading.light{
  background-color: #f5f7f9;
  color: #555
}

#page_loading.light a{
  color: #666;
  border-color: #555
}

#page_loading.light a: before{
  background-color: #666
}

#page_loading.light .loader{
  background-color: #e5e9eb
}

#page_loading.light .loader>div>div{
  background-image: linear-gradient(to bottom,#eaecee,transparent)
}

#page_loading.dark{
  background-color: #272727;
  color: #e5e5e5
}

#page_loading.dark a{
  color: #fff;
  border-color: #ccc
}

#page_loading.dark a: before{
  background-color: #ddd
}

#page_loading.dark .loader{
  background-color: transparent
}

#page_loading.dark .loader>div>div{
  background-image: linear-gradient(to bottom,#292929,transparent)
}

#page_loading .nojs{
  position: absolute;
  width: 300px;
  top: 50%;
  left: 50%;
  margin-left: -150px;
  margin-top: 50px;
  text-align: center;
  font: 400 18px 'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif
}

#page_loading .nojs strong,#page_loading .nojs b{
  font-weight: 800
}

#page_loading .nojs a{
  font-size: 80%;
  text-decoration: none;
  border-style: dotted;
  border-width: 0 0 1px;
  position: relative
}

#page_loading .nojs a: before{
  content: '';
  position: absolute;
  width: 100%;
  height: 1px;
  bottom: -1px;
  left: 0;
  visibility: hidden;
  -webkit-transform: scaleX(0);
  transform: scaleX(0);
  -webkit-transition: all .3s ease-in-out 0;
  transition: all .3s ease-in-out 0
}

#page_loading .nojs a: hover: before{
  visibility: visible;
  -webkit-transform: scaleX(1);
  transform: scaleX(1)
}

@media all and (max-width: 414px) {
  .loader{width:320px; height:4px; margin:-2px 0 0 -160px}
  .loader>div{background-size: 320px 4px}
}
@media all and (max-width: 320px) {
  .loader{width:250px; height:4px; margin:-2px 0 0 -125px}
  .loader>div{background-size: 250px 4px}
}

Важная заметка — в теле примера реализованы два готовых «стиля» индикатора — темный, и светлый. Применяются они путем указания класса dark или light соответственно к тегу div#page_loading. Так же после адаптации кода под ваш сайт/проект полученный CSS код лучше всего пропустить через CSS compressor (в случае необходимости его поправить там же его можно и «разжать»)

Давай теперь взглянем что же у нас получилось (сообщение о необходимости включения js выводится для демонстрации и только):

Открыть демо (превью)

Теперь перейдем к сокрытию индикатора. Для этого мы будем использовать небольшой js-скрипт после загрузки всех скриптов (в самом конце страницы). Всё что он делает — это с интервалом в 10 мс. проверяет document.readyState, и как только последний получит статус complete, то:

  • Скрывает (и удаляет из DOM) индикатор загрузки;
  • Удаляет класс noscroll у тега <body />;
  • Останавливает работу проверяющего цикла;

Посмотрим на его исходник:

// Hide loading overlay <div /> (with spinner) after page complete loading. After hiding,
//   <div /> will be removed, and for <body> removed css class 'noscroll'
var _loading_spinner=setInterval(function(){if(document.readyState=='complete'){
  var $page_loading = document.getElementById('page_loading'),
      $body = document.body || document.getElementsByTagName('body')[0],
      speed = 300, delay = 300;
  if((typeof $page_loading != 'undefined') && ($page_loading != null)){
    setTimeout(function(){
      var transition = 'opacity ' + speed.toString() + 'ms ease',
          removeCssClass = function(obj, className){
            obj.className = obj.className.replace(className, '').replace('  ', ' ');
          };
      ['-webkit-transition','-moz-transition','transition'].forEach(function(prefix){
        $page_loading.style[prefix] = transition;
      });
      $page_loading.style['opacity'] = '0';
      $page_loading.style['filter']  = 'alpha(opacity=0)';
      removeCssClass($body, 'noscroll');
      setTimeout(function(){
        $page_loading.parentNode.removeChild($page_loading);
      }, speed + 10);
    }, delay);
  }
  clearInterval(_loading_spinner);
}},10);

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

Открыть демо (результат)

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

Ссылки
  1. Как это прелоадер разместить на сайте под управлением Джумлы 3?

    • Модифицировать файлы темы, добавив необходимый код в <head>, контейнеры сразу после открывающего тега <body> и собственно тело скрипта перед его закрытием.

  2. Чувак, проверь свой скрипт remove_MS_telemetry.cmd он добавляет какието хосты которые блокируют норальную работу скайпа, половина людей оффлайн, сообщения то доходят то нет, месяц так сидел думал проблема у скайпа, почистил хост файл и скайп заработал..

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

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

      • Пока грешу на запись:

        0.0.0.0 s.gateway.messenger.live.com

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

        0.0.0.0 spynetalt.microsoft.akadns.net
        0.0.0.0 spynetalt.microsoft.com
        0.0.0.0 spyneteurope.microsoft.akadns.net
        0.0.0.0 sqm.df.telemetry.microsoft.com
        0.0.0.0 sqm.telemetry.microsoft.com
        0.0.0.0 sqm.telemetry.microsoft.com.nsatc.net
        0.0.0.0 ssw.live.com
        0.0.0.0 ssw.live.com.nsatc.net

        • Скрипт обновил, можете проверить :) Не забудьте только перед запуском грохнуть все что было ранее добавлено скриптом в файл хостов (как это сделать — описано дополнительно в самом посте)!

Комментирование наглухо закрыто