Введение

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

На эту тему я тоже уже писал ранее, но, как это обычно и бывает, возникают новые потребности, ищутся и, порой, находятся пути их реализации. Если вам интересно, что же это за потребности и как я их реализовал - читайте дальше. Но прежде, хочу предупредить, что web дизайн, все эти CSS, Javascript и прочие прибамбасы не входят в сферу моих профессиональных интересов, я лишь упражняюсь, как любитель. Если бы Ghost позволил мне обойтись без всех этих премудростей, я был бы только счастлив.

Добавление подписи к рисункам

Первое, что мне захотелось - получить возможность задавать поясняющую подпись под вставленным изображением. На самом деле, можно придумать и реализовать множество вариантов. Например, можно вставить таблицу из двух строк, в одной разместив картинку, во второй - подпись. Можно воспользоваться специальной карточкой для изображений, которые появились в Ghost в версии 2, если память мне не изменяет. Можно, наконец, воспользоваться какой-нибудь другой темой, а не Casper-ом, который является темой по умолчанию. Но... Таблицы добавлять достаточно сложно, специальная карточка сразу отметала использование мобильного клиента[1], ну и заменять тему тоже не хотелось, так как Casper пактически сразу получает новые фичи и исправления ошибок.

Тег <figure/>

На самом деле, мне очень приглянулось решение, основанное на использовании тегов <figure> и <figcaption>, которые появились в спецификации HTML5. Тег <figure/> и предназначен для описания как раз таких объектов, как иллюстрации, диаграммы, исходные коды и тому подобное. Ну а <figcaption/> используется для указания описания элемента <figure/>, то есть, той самой подписи.

Главное ограничение, если это можно назвать ограничением, заключается в том, что, с идеологической точки зрения, содержимое тега <figure/> должно быть самодостаточным и не привязанным к конкретному месту в документе. То есть, предполагается, что в тексте есть смысловая ссылка, например, "смотри рисунок 22", а где этот рисунок размещен - хоть в самом конце текста, хоть рядом с этой самой ссылкой - не имеет значения, главное, что он должен быть подписан "Рисунок 22". А вот если в тексте написано "смотри рисунок ниже", то предлагается не использовать тег <figure/>, а задействовать, например, тег <div/>.

Code injection

Ну вот, с тем, как описывать рисунки с подписями, я определился. Далее дело за малым - выбрать механизм, как добавлять эти теги в текст. И самый простой способ - руками, по мере написания текста поста. Но у такого метода, наряду с его основным преимуществом (простотой), есть ряд отрицательных моментов. Один из них - что делать с теми постами, которые уже опубликованы? Неужели, все их редактировать? Кроме того, вбивать каждый раз эти HTML элементы в Markdown тексте - то ещё удовольствие. И тут на помощь мне пришел великий и могучий Javascript. Но, для начала, следует упомянуть о такой возможности Ghost, как Code injection.

Ghost использует шаблонизатор Handlebars для реализации собственного фреймворка шаблонов. В этом инструменте есть такое понятие, как helper-ы. Что это такое? Если коротко, то helper - это предварительно определенная функция, результат выполнения которой будет вставлен в шаблон. Причем, некоторые helper-ы реализованы, что называется, из коробки и, кроме того, предусмотрен механизм написания и регистрации собственных helper-ов.

Авторы Ghost не поленились и написали много собственных helper-ов. В их числе есть {{ghost_head}} и {{ghost_foot}}, которые можно использовать в собственных темах. Сами разработчики Ghost задействуют эти helper-ы в собственной теме Casper - они добавили их в файл default.hbs: {{ghost_head}} - непосредственно перед закрывающим тегом </head>, а {{ghost_foot}} - непосредственно перед закрывающим тегом </body>. Выглядит это так:

<html lang="{{@site.lang}}">
<head>
...
    <link rel="stylesheet" type="text/css" href="{{asset "built/screen.css"}}" />

    {{!-- This tag outputs SEO meta+structured data and other important settings --}}
    {{ghost_head}}

</head>
<body class="{{body_class}}">
...
    {{!-- The #block helper will pull in data from the #contentFor other template files. In this case, there's some JavaScript which we only want to use in post.hbs, but it needs to be included down here, after jQuery has already loaded. --}}
    {{{block "scripts"}}}

    {{!-- Ghost outputs important scripts and data with this tag - it should always be the very last thing before the closing body tag --}}
    {{ghost_foot}}

</body>
</html>

Использование этих helper-ов предоставляет довольно много возможностей, в том числе - добавление дополнительных скриптов и стилей. Естественно, чтобы эти скрипты и стили можно было добавить, необходимо место, в котором их можно было бы задать. И авторы Ghost предоставли такую возможность в административном интерфейсе, назвав ее Code injection. На самом деле, вся эта конструкция получилась достаточно удобной, так как позволяет быстро внести дополнения в чужую тему, по сути, не меняя ее[2].

Javascript для формирования тегов <figure/>

Итак, благодаря Code injection есть возможность в тему добавить некий Javascript код, который бы что-нибудь этакое бы сделал, после чего волшебным образом у нас все рисунки обрамились бы тегом<figure/> с подписями в теге <figcaption/>. Я не очень большой знаток Javascript, поэтому не стал сам изобретать велосипед, предположив, что кто-то где-то такое уже делал. И предчувствие меня не обмануло: я наткнулся на тему для Ghost, которую ее автор выложил в открытый доступ, поняв (и приняв), что не имеет достаточно времени для ее развития. И за эту щедрость ему огромное человеческое спасибо.

В этой теме (название ее Chiara) есть именно тот функционал, который мне и требовался. В файле index.js я нашел интересующий меня кусочек кода:

...
// Creates Captions from Alt tags
$(".post-content img").each(function() {
    // Let's put a caption if there is one
    if($(this).attr("alt"))
        $(this).wrap('<figure class="image"></figure>')
            .after('<figcaption>'+$(this).attr("alt")+'</figcaption>');
});
...        

Давайте попробуем разобраться, что здесь к чему. Автор кода выбирает все теги <img/>, которые принадлежат элементу с классом post-content. То есть, изменения коснутся только тех графических изображений, которые включены в текст поста. Другие картинки, например, заглавная картинка поста, не пострадают.

После, уже в теле цикла, для каждого найденного элемента <img/> производится проверка, определен ли для этого элемента атрибут alt. То есть, автор решил для себя, что именно атрибут alt он будет использовать для задания подписи к картинке, и если такого атрибута у конкретного элемента нет, то и модифицировать этот элемент не надо. Что ж, на самом деле, весьма логично.

Ну а дальше следует код с использованием методов библиотеки jQuery, который и выполняет всю работу:

  • обрамляет (метод wrap) найденный элемент <img/> HTML кодом <figure class="image"></figure>, по сути, элементом <figure/>, задавая ему класс image
  • добавляет за найденным элементом (метод after) HTML текст '<figcaption>'+$(this).attr("alt")+'</figcaption>', то есть, добавляет элемент <figcaption/>, в качестве значения которого указывается значение атрибута alt обрабатываемого тега <img/>

Таблица стилей - CSS

Но изменить/добавить нужный HTML код - это пол дела. Надо еще определить, как этот код будет представлен. А для этого используются таблицы стилей. Если обратиться к первоисточнику, а именно, к теме Chiara, то в файле main.sass[3] можно найти объявление стиля для следующих элементов <figure/> и <figcaption/>:

...
.post-content .post-footer
...
            figure
                padding: 0 0 modular-scale(1rem, 2, 1.33)
                margin: 0
                text-align: center
                figcaption
                    padding: modular-scale(1rem, -1, 1.33) 0 0
                    font-style: italic
                    color: #aaa
                    font-size: modular-scale(1rem, 0, 1.33)
...

Как видим, автор темы задает отступы, поля и выравнивание для тега <figure/> и предлагает использовать курсивный шрифт (italic), размер которого рассчитывается[4] исходя из размера шрифта корневого элемента html[5], в качестве цвета использовать #aaa - средне-светлый оттенок серого, ну и отступы (padding) тоже определены относительно шрифта корневого элемента. Использовать этот стиль, или создать собственный - решать вам.

Решение

Теперь соберем все сказанное ранее воедино. В административном интерфейсе блога надо выбрать элемент меню Code injection и добавить наш стиль и скрипт в соответствующие элементы. Обычно, стили добавляются в helper {{ghost_head}}, а скрипты - в helper {{ghost_foot}}:

Административный интерфейс блога

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

<style>  
.post .post-content figcaption {
    font-weight: normal;
    font-style: italic;
    font-size: 16px;
    color: #555;
    outline: 0;
    text-align: center;
}

Ну и сам скрипт для вставки нужных HTML элементов:

<script>  
    // Creates Captions from Alt tags
    $(".post-content img").each(
        function() {
            // Let's put a caption if there is one
            if ($(this).parent().prop("tagName").toLowerCase() !== 'figure' && $(this).attr("alt")) {
                $(this).wrap(
                    '<figure class="image"></figure>'
                ).after('<figcaption>' + $(this).attr("alt") + '</figcaption>');
            }
        });
</script>

В скрипте я добавил лишь одну вещь - проверку на то, не "обернут" ли уже тег <img/> в тег <figure/>. Если обернут, то я считаю, что, по каким-то причинам, все необходимые действия были сделаны мною вручную, и автоматическая обработка не требуется. Если вам такая проверка не нужна, то просто уберите следующий код: $(this).parent().prop("tagName").toLowerCase() !== 'figure' && .

Вот, пожалуй, и все... пока...


  1. Это сейчас мобильный клиент не работает и авторы даже убрали его из магазина приложений, тогда же он работал только при условии, что текст поста содержит одну единственную Markdown карточку ↩︎

  2. Если, конечно, автор такую возможность предусмотрел, добавив в свою тему пару этих helper-ов. ↩︎

  3. Что такое sass можно почитать тут. ↩︎

  4. Что такое modular-scale можно посмотреть тут ↩︎

  5. Про rem можно почитать, например, тут ↩︎