Заметки с тегом «hint» (страница 3)

Охота за последним видимым элементом

Последний элемент среди соседей можно выбрать селектором псевдо-класса :last-child.


.item { display: block; }
.item:last-child { font-weight: bold; }

Разметка:


<div>
  <div class="item item_rank_1">Object “A”</div>
  <div class="item item_rank_1">Object “B”</div>
  <div class="item item_rank_0">Object “C”</div>
</div>

Но, если с помощью медиа-запроса скрыть часть элементов списка, то получить последний видимый элемент уже так просто не выйдет.


@media (max-width: 600px) {
  .item_rank_0 { display: none; }
}

Казалось бы можно использовать селектор .item_rank_1:last-child , но это так не работает. Псевдо-класс :last-child позволяет найти только одного последнего ребёнка своего родителя.

Но мы уже живём в мире HTML5, где можно грабить корованы и придумывать свои собственные теги (оригинал ). По этому вместо того, чтобы отличаться классами, элементы будут отличаются названиями тегов.


<div>
  <item-rank-1 class="item">Object “A”</item-rank-1>
  <item-rank-1 class="item">Object “B”</item-rank-1>
  <item-rank-0 class="item">Object “C”</item-rank-0>
</div>

Да, так тоже можно, если очень хочется.

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


@media (max-width: 600px) {
  item-rank-0.item { display: none; }
  item-rank-1:last-of-type { font-weight: bold; }
}

Таким способом можно создавать любые теги и они будут прекрасно поддаваться оформлению через CSS. Для IE < 9 нужно будет их предварительно создать с помощью JavaScript так как делаем это для новых HTML5 элементов.

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

Живой пример

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

Комментарии к заметке: 21

Аргументы функции

Узнать, сколько функция принимает аргументов, можно в её свойстве length.

var l = (function (a, b, c) {}).length; // == 3

Узнать, сколько в неё было передано аргументов, можно в свойстве length объекта arguments.

(function (a, b, c) {
  var l = arguments.length; // == 2
})(100, 200);

Хоть объект arguments и похож на массив, но таковым он не является. У него нет методов, которые есть у массивов. Чтобы превратить его в настоящий массив нужно сделать его независимую копию.

var args = Array.prototype.slice.call(arguments);
Комментарии к заметке: 4

Кеширование медленных вычислений

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

В библиотеке Underscore.js есть метод, который для любой функции делает версию этой функции с кешированием — memoize.

Так, например, её можно применять для кеширования _.template().


var View = Backbone.View.extend({
  render: function () {
    var markup = _.template(this.template, this.model.toJSON());
    this.$el.html(markup);
  }
});

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


var cachedTemplate = _.memoize(_template);
var View = Backbone.View.extend({
  render: function () {
    var markup = cachedTemplate(this.template, this.model.toJSON());
    this.$el.html(markup);
  }
});

Разница в производительности при рендеринге простенького шаблона и его кешированного варианта налицо.

Все скомпилированные шаблоны находятся в хранилище ключ-значение внутри замыкания _.memoize(). Ключом в нашем случае выступает текст шаблона, а значением — скомпилированная функция.

В качестве ключа может быть не только текстовая строка. Если первый параметр кешируемой функции не является строкой, то ключом станет то, что венёт его метод .toString() . В некоторых случаях это может оказаться неприемлемо (например, когда первым аргументом идёт объект). Чтобы избежать коллизий, в _.memoize() можно передать опциональную функцию, генерирующую ключ из аргументов.


var cachedTemplate = _.memoize(_.template, function (value) {
  return JSON.stringify(value);
});

Аналогичный приём, но без использования Underscore.js я применяю для кеширования асинхронных запросов.

Комментарии к заметке: 3

Повседневные задачи для команды OpenSSL

Мне порой бывает нужно закодировать содержимое файла в base64 или наоборот раскодировать. В этом случае выручает команда openssl. Она, скорее всего, уже установлена в большинстве ОС. И 100% есть в Windows, если вы пользуетесь Git.

Содержимое файла кодируется следующей командой

openssl base64 <file.bin >file.base64

Если в выходных данных не нужны переносы строк, то их легко можно убрать

openssl base64 <file.bin | tr -d '\n' >file.base64

Декодируется файл аналогично с дополнительным ключом -d

openssl base64 -d <file.base64 >file.bin

Вообще, команда openssl – это многофункциональный комбайн. Он может создавать ключи и сертификаты для подписи и шифрования, вычислять контрольные суммы (message digests), шифровать и расшифровывать данные, проверять подпись, тестировать SSL/TLS клиенты и серверы.

Так, например, можно подсчитать md5-сумму файла

openssl md5 <file.bin

или строки текста

echo "Hello" | openssl md5
Оставте свой комментарий

Компилируемые шаблоны и способы их хранения

Я уже писал в одной из заметок о шаблонах на JS . В этот раз я рассмотрю диаметральный подход – компилируемые шаблоны. Примерами таких шаблонов могут служить mustache.js, Underscore template и многие другие библиотеки.

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

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

  • в виде строки в js-файле;
  • внутри тега <script> с произвольным типом в html;
  • в отдельном файле;
  • компилирование исходного текста шаблона при сборке проекта.

Хранение в js-файле


var templateSource = "<div class=\"welcome\">" +
    '<p class="welcome__message">Hello: <%= name %></p>' +
    '</div>';

var template = _.template(templateSource);

Плюсы:

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

Минусы:

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

Хранение в исходном HTML-коде страницы


<script id="template-welcome" type="text/x-template">
    <div class="welcome">
        <p class="welcome__message">Hello: <%= name %></p>
    </div>
</script>

Браузер содержимое тега <script> считает простым текстом, а так как в атрибуте type у него указан неизвестный ему MIME-тип, то интерпретировать или отображать он его не станет. Зато содержимое этого тега можно получить после загрузки документа, обратившись к нему по id.

var templateSource = document.getElementById("template-welcome").innerText;

Плюсы:

  • размещается вместе с другой разметкой;
  • набор шаблонов или их содержимое может динамически меняться в зависимости от внешних факторов.

Минусы:

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

Хранение в отдельном файле

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


<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <template id="template-welcome"><![CDATA[
        <div class="welcome">
            <p class="welcome__message">Hello: <%= name %></p>
        </div>
    ]]></template>
    <template id="template-product"><![CDATA[
        …
    ]]></template>
</templates>

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


$.ajax("/static/templates/welcome.xml")
    .done(function (templates) {
        var templateEle = templates.getElementById("template-welcome");
        if (templateEle) {
            callback(_.template(templateEle.innerText));
        }
        templateEle = null;
    });

Набор шаблонов загружается с помощью XHR, а затем методом getElementById() получаем нужный объект, содержащий исходный текст шаблона.

Плюсы:

  • с большой вероятностью статический файл, подгружаемый через XHR, будет закеширован браузером, и в дальнейшем некоторое время не будет запрашиваться с сервера;
  • можно организовать дополнительный уровень кеширования шаблонов (Local Storage, например);
  • наборы шаблонов можно формировать для конкретной комбинации ролей пользователя, что позволит защитить функциональность недоступную текущему пользователю.

Минусы

  • дополнительный HTTP-запрос при первом посещении;
  • инициализация модуля будет «отложенной» из-за асинхронной загрузки требуемых шаблонов.

Компилирование при сборке

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

Плюсы:

  • шаблоны будут кешироваться браузером как любые другие статические ресурсы;
  • браузер не тратит время на компиляцию шаблона.

Минусы:

  • для работы нужен процесс сборки проекта;
  • для компилирования шаблонов при сборке потребуется соответствующая технология (Node.js или нативная реализация на соответствующем языке программирования), что может быть накладно.

Заключение

Метод хранения сложных шаблонов в отдельных статических файлах мне кажется достаточно перспективным по сравнению с хранением тех же данных в HTML. С другой стороны, инициализация модуля оказывается гораздо сложнее, чем в случае с хранением шаблона в JS. Компилирование шаблона при сборке может стать идеальным вариантом, если в проекте есть сборка как таковая.

Комментарии к заметке: 6