CSS: выделение текста жирным шрифтом без изменения размера контейнера

119

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

Проблема в том, что при выделении жирным шрифтом слово становится немного шире, что приводит к небольшому смещению остальных элементов влево или вправо. Есть ли хитрый способ предотвратить это? Что-то вроде указания обивке игнорировать лишнюю ширину, вызванную полужирным шрифтом? Моей первой мыслью было просто вычесть несколько пикселей из заполнения «активного» элемента, но это количество варьируется.

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

скуловая кость
источник
почему изменение размера проблема? Это как-то портит макет?
Chaulky
Я думаю, что вопрос о веб-программировании лучше всего задавать в Stack Overflow. Возможно, мод перенесет это туда.
Ciaran
2
Chaulky: потому что есть несколько вещей, которые я ненавижу больше в плане расположения, чем когда кнопки, которые вы должны пытаться нажимать, прыгают, прыгая
Мала
doejo.com/blog/… - одно из найденных мной решений, которое делает именно то, о чем говорили Джон и Блоски, jscript.
thebulfrog
2
Возможный дубликат смещения встроенных элементов при
Бальтазар

Ответы:

152

У меня была та же проблема, но я получил аналогичный эффект с небольшим компромиссом, вместо этого я использовал text-shadow.

li:hover {text-shadow:0px 0px 1px black;}

Вот рабочий пример:

body {
  font-family: segoe ui;
}

ul li {
  display: inline-block;
  border-left: 1px solid silver;
  padding: 5px
}

.textshadow :hover {
  text-shadow: 0px 0px 1px black;
}

.textshadow-alt :hover {
  text-shadow: 1px 0px 0px black;
}

.bold :hover {
  font-weight: bold;
}
<ul class="textshadow">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>text-shadow: 0px 0px 1px black;</code></li>
</ul>

<ul class="textshadow-alt">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>text-shadow: 1px 0px 0px black;</code></li>
</ul>

<ul class="bold">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li><code>font-weight: bold;</code></li>
</ul>

пример jsfiddle

Торгер
источник
51
Мне нравится это решение, хотя я рекомендую перевернуть его на 1px 0px 0px. Выглядит более смелым.
M. Herold
Мне нужно было решение только для css, и это ответ.
JCasso,
ты настоящий гений! простота делает это потрясающе!
lufi
1
Только добавление li:hover {text-shadow: 1px 0 0 black;}привело к тексту с слишком маленьким интервалом между буквами. Чтобы еще раз улучшить это, мы также установилиli {letter-spacing: 1px;}
Патрик Хаммер
5
Я использовал -0.5px 0 #fff, 0.5px 0 #fff, потому что мы могли видеть небольшой промежуток между самим текстом и тенью. А также, когда текст выделен жирным шрифтом, он добавляет веса обеим сторонам.
atorscho
101

Лучшее рабочее решение с использованием :: after

HTML

<li title="EXAMPLE TEXT">
  EXAMPLE TEXT
</li>

CSS

li::after {
  display: block;
  content: attr(title);
  font-weight: bold;
  height: 1px;
  color: transparent;
  overflow: hidden;
  visibility: hidden;
}

Он добавляет невидимый псевдоэлемент с шириной полужирного текста, полученный по titleатрибуту.

text-shadowРешение выглядит неестественно на Mac и не использует всю красоту , что текст рендеринга на Mac предложений .. :)

http://jsfiddle.net/85LbG/

Кредит: https://stackoverflow.com/a/20249560/5061744

workaholic_gangster911
источник
11
Это наиболее надежное и чистое решение, оно просто резервирует место для полужирного текста в элементе. В моем случае дополнительный псевдоэлемент вызвал изменение высоты. Добавление негатива margin-topк ::afterблоку решило эту проблему.
Вацлав Новотны
2
Отличное решение! Я использовал JS для добавления атрибута data-content, который содержит текст элемента, чтобы я мог избежать изменения HTML.
mistykristie
1
Потрясающие. Это должен быть лучший ответ. Обратите внимание: если элемент отличается от li, возможно, вам придется использовать display: block; на: после родительского элемента.
Blackbam
Отличное решение! Прекрасно работает с :: before также, в моем случае это предотвращает дополнительный запас внизу.
Thomas Champion
Спасибо за это решение. Изначально я использовал text-shadow, но в Safari это не работает, поскольку округляет десятичные значения. Поскольку я использовал 0,1 пикселя, оно округлялось до 0. Ваше решение работало безупречно. Я заменил только атрибут title на имя, так как не хотел, чтобы всплывающие подсказки отображались
Джонатан Кардоз
32

Самым портативным и визуально приятным решением будет использование text-shadow. Это пересматривает и показывает примеры ответа Торгейра с использованием Alexxali и моих собственных настроек:

  li:hover { text-shadow: -0.06ex 0 black, 0.06ex 0 black; }

Это помещает крошечные «тени» в черный цвет (используйте название цвета / код вашего шрифта вместо, blackесли необходимо) с обеих сторон каждой буквы, используя единицы, которые будут правильно масштабироваться с рендерингом шрифта.

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

В этом ответе используются доли exединиц, поскольку они масштабируются вместе со шрифтом.
В ~ большинстве настроек браузера по умолчанию * ожидайте 1ex8pxи, следовательно, 0.025ex0.1px.

Посмотреть на себя:

li             { color: #000; } /* set text color just in case */
.shadow0       { text-shadow: inherit; }
.shadow2       { text-shadow: -0.02ex 0 #000, 0.02ex 0 #000; }
.shadow4       { text-shadow: -0.04ex 0 #000, 0.04ex 0 #000; }
.shadow6       { text-shadow: -0.06ex 0 #000, 0.06ex 0 #000; }
.shadow8       { text-shadow: -0.08ex 0 #000, 0.08ex 0 #000; }
.bold          { font-weight: bold; }
.bolder        { font-weight: bolder; }
.after span        { display:inline-block; font-weight: bold; } /* workaholic… */
.after:hover span  { font-weight:normal; }
.after span::after { content: attr(title); font-weight: bold; display:block; 
                     height: 0; overflow: hidden; }
.ltrsp         { letter-spacing:0; font-weight:bold; } /* @cgTag */
li.ltrsp:hover { letter-spacing:0.0125ex; }
li:hover       { font-weight: normal!important; text-shadow: none!important; }
<li class="shadow0">MmmIii123 This line tests shadow0 (plain)</li>
<li class="shadow2">MmmIii123 This line tests shadow2 (0.02ex)</li>
<li class="shadow4">MmmIii123 This line tests shadow4 (0.04ex)</li>
<li class="shadow6">MmmIii123 This line tests shadow6 (0.06ex)</li>
<li class="shadow8">MmmIii123 This line tests shadow8 (0.08ex)</li>
<li class="after"><span title="MmmIii123 This line tests [title]"
                   >MmmIii123 This line tests [title]</span> (@workaholic…)</li>
<li class="ltrsp"  >MmmIii123 This line tests ltrsp (@cgTag)</li>
<li class="bold"   >MmmIii123 This line tests bold</li>
<li class="bolder" >MmmIii123 This line tests bolder</li>
<li class="shadow2 bold">MmmIii123 This line tests shadow2 (0.02ex) + bold</li>
<li class="shadow4 bold">MmmIii123 This line tests shadow4 (0.04ex) + bold</li>
<li class="shadow6 bold">MmmIii123 This line tests shadow6 (0.06ex) + bold</li>
<li class="shadow8 bold">MmmIii123 This line tests shadow8 (0.08ex) + bold</li>

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

Измените уровень масштабирования вашего браузера ( Ctrl+ +и Ctrl+ -), чтобы увидеть, как они меняются.

Я добавил здесь два других решения для сравнения: трюк с интервалом между буквами @ cgTag , который работает не так хорошо, поскольку предполагает угадывание диапазонов ширины шрифта, и трюк @ workaholic_gangster911 :: after рисования , который оставляет неудобное дополнительное пространство, поэтому жирный текст может расширяться без подталкивания соседних текстовых элементов (я помещаю атрибуцию после полужирного текста, чтобы вы могли видеть, как он не перемещается).


В будущем у нас будет больше вариативных шрифтов, способных изменять качество шрифта с помощью font-variation-settings. Поддержка браузеров растет (Chrome 63+, Firefox 62+), но для этого по-прежнему требуется больше, чем просто стандартные шрифты, и лишь немногие существующие шрифты поддерживают его.

Если вы встроите переменный шрифт, вы сможете использовать CSS следующим образом:

/* Grade: Increase the typeface's relative weight/density */
@supports (font-variation-settings: 'GRAD' 150) {
  li:hover { font-variation-settings: 'GRAD' 150; }
}
/* Failover for older browsers: tiny shadows at right & left of the text
 * (replace both instances of "black" with the font color) */
@supports not (font-variation-settings: 'GRAD' 150) {
  li:hover { text-shadow: -0.06ex 0 black, 0.06ex 0 black; }
}

В Mozilla Variable Fonts Guide есть живая демонстрация с ползунком для работы с различными классами. Во введении Google в вариативные шрифты в Интернете есть анимированный GIF-файл, демонстрирующий переключение между высокой и нулевой оценкой:

анимированная демонстрация шрифта Amstelvar Alpha с переключением оси оценок

Адам Кац
источник
1
Так просто, так красиво.
Рэнди Холл
18

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

a {
   letter-spacing: 1px;
}

a:hover {
   font-weight: bold;
   letter-spacing: 0px;
}

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

Reactgular
источник
3
это лучшее и наиболее недооцененное решение, хотя я изменил его так, чтобы оно начиналось с 0 и переходило к отрицательному межбуквенному интервалу при выделении жирным шрифтом. также вы должны настроить его для каждого шрифта и размера шрифта. Я также обновил скрипку @ Thorgeir, чтобы включить это (как второе решение) jsfiddle.net/dJcPn/50/
robotik 01
Современные браузеры теперь поддерживают субпиксельную точность. Таким образом, вы можете точно настроить интервал, используя 0.98pxили меньшие дроби.
Reactgular 03
Это выглядит немного странно, поскольку алгоритм интервалов не совсем такой же (некоторые буквы немного танцуют) и, что более важно, это не работает, когда шрифты масштабируются, даже если вы конвертируете в 1pxотносительные значения, такие как 0.025exили 0.0125ex. Смотрите мой ответ для живой демонстрации.
Адам Кац
@AdamKatz Я подозреваю, что с момента публикации этого ответа рендеринг шрифтов изменился, и это во многом зависит от того, какой шрифт вы используете.
Reactgular 03
5

Для более актуального ответа вы можете использовать -webkit-text-stroke-width:

.element {
  font-weight: normal;
}

.element:hover {
  -webkit-text-stroke-width: 1px;
  -webkit-text-stroke-color: black;
}

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

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

Я определенно работает в Edge, Firefox, Chrome и Opera (на момент публикации) и в Safari ( отредактируйте: @Lars Blumberg, спасибо за подтверждение ). Он НЕ работает в IE11 или ниже.

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

Оливер
источник
1
Также работает в Safari 13.0.5. Пока что самое чистое решение для меня.
Ларс Блумберг,
4

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

Единственное, что я могу придумать, - это использовать некоторый javascript, который вычисляет ширину вкладки до того, как она будет выделена жирным шрифтом, а затем применяет ширину, в то же время, когда требуется жирный шрифт (либо при наведении курсора, либо при нажатии).

ajcw
источник
хм, я буду экспериментировать с этим, хотя я хочу, чтобы это выглядело, по крайней мере, разумно (т.е. выделено жирным шрифтом) для пользователей, не использующих JS. Может быть, я могу отправить его полужирным шрифтом, использовать JS, чтобы выделить его, вычислить ширину, а затем снова выделить его? Или это просто глупо?
Мала
Да, похоже, это лучший способ приспособиться к пользователям, не использующим JavaScript.
ajcw
2

Используйте JavaScript, чтобы установить фиксированную ширину li на основе содержимого без жирного шрифта, затем выделите содержимое жирным шрифтом, применив стиль к <a>тегу (или добавьте диапазон, если у <li>него нет дочерних элементов ).

Дэн Блоуз
источник
Упс - извините за повторение: $
Dan Blows
В любом случае спасибо за ответ :)
Мала
@Mala Вы нашли решение? Как ни странно, у меня похожая проблема.
Дэн Блоуз
1

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

(Пропустите этот абзац для TL; DR ...)Я использую шрифт Gotham с сайта cloud.typography.com, и у меня есть кнопки, которые начинаются пустыми (с белой рамкой / текстом и прозрачным фоном) и приобретают цвет фона при наведении курсора. Я обнаружил, что некоторые цвета фона, которые я использовал, плохо контрастировали с белым текстом, поэтому я хотел изменить текст на черный для этих кнопок, но - из-за визуального трюка или обычных методов сглаживания - темный текст на светлом фоне всегда кажется более светлым, чем белый текст на темном фоне. Я обнаружил, что увеличение веса темного текста с 400 до 500 обеспечивает почти такой же «визуальный» вес. Тем не менее, он увеличивал ширину кнопки на крошечную величину - доли пикселя - но этого было достаточно, чтобы кнопки выглядели слегка «дрожащими», от чего я хотел избавиться.

Решение:

Очевидно, что это действительно привередливая проблема, поэтому ее нужно было решить. В конечном итоге я использовал негатив letter-spacingдля более жирного текста, как рекомендовал cgTag выше, но 1px был бы излишним, поэтому я просто рассчитал именно ту ширину, которая мне нужна.

Изучив кнопку в Chrome devtools, я обнаружил, что ширина моей кнопки по умолчанию была 165,47 пикселей, а при наведении - 165,69 пикселей, разница 0,22 пикселей. На кнопке было 9 символов, поэтому:

0,22 / 9 = 0,024444 пикселей

Преобразуя это в em-единицы, я мог сделать настройку независимой от размера шрифта. Моя кнопка использовала размер шрифта 16 пикселей, поэтому:

0,024444 / 16 = 0,001527эм

Итак, для моего конкретного шрифта следующий CSS сохраняет кнопки точно такой же ширины при наведении курсора:

.btn {
  font-weight: 400;
}

.btn:hover {
  font-weight: 500;
  letter-spacing: -0.001527em;
}

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

Единственное предостережение заключается в том, что разные браузеры используют несколько разные вычисления субпикселей, поэтому, если вы стремитесь к этому уровню OCD с точностью до субпикселей, вам нужно будет повторить тестирование и установить разные значения для каждого браузера. Стили CSS, ориентированные на браузер, обычно не одобряются по уважительной причине, но я думаю, что это один из вариантов использования, когда это единственный вариант, который имеет смысл.

dannymcgee
источник
0

Интересный вопрос. Я полагаю, вы используете float, верно?

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

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

Дэвис Пейшото
источник
0

Вы можете реализовать это, например, на сайте amazon.com в контекстном меню «Покупки по отделам». Он использует широкий div. Вы можете создать широкий div и скрыть его правую часть

Андрус
источник
0

ОБНОВЛЕНИЕ: пришлось использовать тег B для заголовка, потому что в IE11 псевдокласс i: after не отображался, когда у меня была видимость: скрытый.

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

Предлагаемое здесь решение не сработало для меня в Chrome. Вертикальное выравнивание input и label было испорчено из-за: after psuedo class и -margins не исправили это.

Вот исправление, при котором у вас не возникает проблем с вертикальным выравниванием.

/* checkbox and radiobutton */
label
{
    position: relative;
    display: inline-block;
    padding-left: 30px;
    line-height: 28px;
}

/* reserve space of bold text so that the container-size remains the same when the label text is set to bold when checked. */
label > input + b + i
{
    font-weight: bold;
    font-style: normal;
    visibility: hidden;
}

label > input:checked + b + i
{
    visibility: visible;
}

/* set title attribute of label > b */
label > input + b:after
{
    display: block;
    content: attr(title);
    font-weight: normal;
    position: absolute;
    left: 30px;
    top: -2px;
    visibility: visible;
}

label > input:checked + b:after
{
    display: none;
}

label > input[type="radio"],
label > input[type="checkbox"]
{
    position: absolute;
    visibility: hidden;
    left: 0px;
    margin: 0px;
    top: 50%;
    transform: translateY(-50%);
}

    label > input[type="radio"] + b,
    label > input[type="checkbox"]  + b
    {
        display: block;
        position: absolute;
        left: 0px;
        margin: 0px;
        top: 50%;
        transform: translateY(-50%);
        width: 24px;
        height: 24px;
        background-color: #00a1a6;
        border-radius: 3px;
    }

    label > input[type="radio"] + b
    {
        border-radius: 50%;
    }

    label > input:checked + b:before
    {
        display: inline-block;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%) rotate(45deg);
        content: '';
        border-width: 0px 3px 3px 0px;
        border-style: solid;
        border-color: #fff;
        width: 4px;
        height: 8px;
        border-radius: 0px;
    }

    label > input[type="checkbox"]:checked + b:before
    {
        transform: translate(-50%, -60%) rotate(45deg);
    }

    label > input[type="radio"]:checked + b:before
    {
        border-width: 0px;
        border-radius: 50%;
        width: 8px;
        height: 8px;
    }
<label><input checked="checked" type="checkbox"/><b title="Male"></b><i>Male</i></label>
<label><input type="checkbox"/><b title="Female"></b><i>Female</i></label>

Erik
источник