ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

JavaScript: дерганье меню, шрифты в браузере

Начало: JavaScript: раскрывающееся меню.

В предыдущем посте я решил задачу «Создать раскрывающееся меню» к подразделу 2.1 «Введение в браузерные события» второй части учебника по JavaScript.

В браузере у меня получилось примерно следующее (картинка):

https://ic.pics.livejournal.com/ilyachalov/24714720/103802/103802_original.gif
(Это анимированная картинка в формате GIF. Чтобы эта анимация не отвлекала от чтения поста, я сделал ее в виде ссылки, по которой можно пройти и посмотреть. Ссылка должна открыться в новой вкладке браузера.)

На этой картинке видно, что при разворачивании и сворачивании полученного меню главный пункт меню «Сладости (нажми меня)!» слегка дергается влево-вправо: при разворачивании он смещается немного вправо, а при сворачивании возвращается на то же расстояние влево.

То же самое происходит и в решении от авторов обсуждаемой задачи. Действующий пример их решения можно рассмотреть на странице задачи.

Чтобы рассмотреть проблему подробнее, я временно задал вокруг символов-треугольников красную рамку с помощью указания border: 1px solid red; в их стиле и увеличил эти символы в 8 раз в графическом редакторе. Вот что у меня получилось:



Как видно на этой картинке, высота у символов-треугольников совпадает, а вот ширина отличается на 2 пикселя. А так как эти символы («▶» и «▼») при разворачивании-сворачивании меню заменяют друг друга, то и происходит постоянное смещение (дерганье) главного пункта меню то влево на 2 пикселя, то вправо на 2 пикселя.

Кстати, ниже пойдет речь про коды символов. На первый взгляд кажется, что эти коды отличаются друг от друга в Юникоде и языке HTML. На самом деле, это один и тот же код, только в Юникоде кодирование символов идет числами в шестнадцатиричной системе счисления, а в языке HTML — в десятичной системе счисления. Например, символ в Юникоде с кодом U+25B6 — тот же символ, что в языке HTML с кодом ▶:
25B616 = 965410

В чем же дело?

Для разбора подобных случаев, как оказалось, в браузере (по крайней мере, в моём «Microsoft Edge» на движке «Chromium») есть очень полезный инструмент в инструментах разработчика (F12). Если открыть инструмент «Elements», то там, в DOM-дереве HTML-элементов, для выделенного узла можно просмотреть его стили, в том числе фактически примененные (по-английски «Computed»). В том числе там можно посмотреть шрифты, которые браузер фактически применяет (по-английски «Rendered Fonts») для отображения HTML-страницы. Там же в скобках после названия шрифта указано, сколько разных глифов (символов) из данного шрифта было применено для отображения выделенного узла в DOM-дереве.

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

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

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

В решении обсуждаемой задачи я не указывал в стилях HTML-элементов какой-либо конкретный шрифт. Поэтому браузер для отображения HTML-страницы взял шрифт «Times New Roman», указанный в его настройках по умолчанию. Однако, как оказалось, в той версии этого шрифта, которая установлена в моей операционной системе («Windows 10»), отсутствует символ U+25B6, который используют авторы обсуждаемой задачи для отображения черного треугольника, указывающего вправо (там есть похожий символ, но с кодом U+25BA). В такой ситуации мой браузер подобрал для отображения этого символа другой шрифт — «Cambria Math», в котором требуемый символ есть и отобразил этот символ глифом из этого шрифта.

(В операционных системах «Windows» для поиска конкретного символа в шрифтах есть специальная программа «Таблица символов».)

Получается, что символы «▶» и «▼» на картинке выше, на самом деле, взяты из разных шрифтов — «Cambria Math» и «Times New Roman» соответственно.

Способы решения проблемы

1) Можно попробовать найти в шрифте другой, но похожий на нужный символ, и использовать его. В шрифте «Times New Roman», как я уже упоминал выше, есть символ с кодом U+25BA (в языке HTML его код ►), похожий на нужный U+25B6. Я попробовал это сделать и у меня получилось. Главный пункт меню перестал дергаться. Почему? Потому что ширина места, выделяемого браузером для этих символов (► и ▼) этого шрифта, теперь совпадает.

2) Можно оставить в коде заданной HTML-страницы символы ▶ и ▼, которые были там изначально, но попробовать найти шрифт, в котором будут эти и другие требуемые символы. Например, я в своей операционной системе нашел шрифт «Lucida Sans Unicode», в котором есть все требуемые для обсуждаемой задачи символы, в том числе проблемный U+25B6. И действительно, когда я использовал этот шрифт в стиле главного пункта нашего меню указанием font-family: Lucida Sans Unicode;, то главный пункт меню перестал дергаться. Ширина места для глифов указанных символов в браузере для этого шрифта снова совпала. Примеры других шрифтов, содержащих символ U+25B6: «Cambria», «Cambria Math», «Malgun Gothic».

При этом могут найтись такие шрифты, в которых есть оба нужных символа U+25B6 и U+25BC, но они будут разной ширины. Тогда этот способ не сработает. Пример такого шрифта: «MS Gothic». Для этого шрифта, кстати, не сработает и первый способ, так как в нем символы U+25BA и U+25BC тоже имеют разную ширину. Получается, оба предложенных способа не универсальны (подходят не для любого шрифта).

Моноширинность

С самого начала мне пришла в голову мысль, что дело может быть в том, что я использую не моноширинные шрифты (в частности, шрифт «Times New Roman»), в которых, как известно, ширина разных символов может быть разной. Например, в шрифте «Times New Roman» у символов «м» и «и» ширина разная. Но при этом в таких шрифтах многие символы имеют одинаковую ширину. Например, в шрифте «Times New Roman» у символов «н» и «и» ширина одинаковая.

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

Я пробовал в стиле главного пункта меню просто прописать указание font-family: monospace;, но у меня это не сработало. Почему? Потому что мой браузер подобрал моноширинный шрифт «Consolas» для отображения главного пункта меню, но в этом шрифте тоже нет символа U+25B6 (по крайней мере, в той его версии, которая установлена в моей операционной системе), поэтому браузер опять же взял этот символ из другого шрифта — того же «Cambria Math», который уже упоминался выше. Ширина у символов U+25B6 и U+25BC не совпала и меню при разворачивании-сворачивании дергаться не перестало.

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

Настройка ширины с помощью CSS

Наверное, самый правильный способ избавиться от вышеописанного дерганья главного пункта меню — это настройка ширины HTML-элемента, включающего символ U+25B6, и отдельно — HTML-элемента, включающего символ U+25BC, так, чтобы их ширина совпадала.

В нашем случае речь идет о HTML-элементах span с идентификаторами rightTri и downTri.

Тут есть одна сложность: мы можем в стиле или из скрипта на языке JavaScript попытаться изменить ширину HTML-элемента span, но ничего не произойдет, ширина не изменится. Это потому, что HTML-элемент является строчным (по-английски «inline») HTML-элементом (его CSS-свойство display по умолчанию равно значению inline), а такие HTML-элементы, как я понимаю, не трансформируются браузером по ширине. Изменить эту ситуацию можно явно, к примеру, прописав в стиле HTML-элемента span указание display: inline-block;. Или неявно — прописав в стиле HTML-элемента span, например, указание float: left;.

Я немного поменял код из предыдущего поста, чтобы получить рабочий пример.

Меняем стиль (добавил одну строку):
  #rightTri, #downTri { /* стиль треугольников */
    font-size: 80%;
    color: #008000;
    float: left;        /* чтобы можно было менять ширину span */
  }

Немного изменил код в теле HTML-страницы:
<span id="main">
  <span id="rightTri">&#9654;&nbsp;</span>
  <span id="downTri">&#9660;&nbsp;</span>
  Сладости (нажми меня)!
</span>
<ul id="items" hidden>
  <li>Пирожное</li>
  <li>Пончик</li>
  <li>Мёд</li>
</ul>

И самое интересное, меняем код скрипта на языке JavaScript (новое вставлено в начале в виде 4 строк):
let w = downTri.offsetWidth + "px"; // получаем ширину более широкого элемента
rightTri.style.width = w;           // назначаем эту ширину двум элементам,
downTri.style.width = w;            // которые должны быть равны
downTri.hidden = true;              // этот элемент в начале должен быть скрыт

main.onclick = openMenu;

function openMenu() {
    rightTri.hidden = !rightTri.hidden;
    downTri.hidden = !downTri.hidden;
    items.hidden = !items.hidden;
}

Теперь наше меню, по идее, не будет дергаться.
Tags: Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments