Zero width space что это
Все о пробелах
Пробелы это не только та большая клавиша, с помощью которой вы разделяете слова в тексте. В этой статье мы рассмотрим дополнительные символы пробелов, их назначение и возможности современного использования.
Ниже вы видите два твита. В одном из них Пол Айриш уведомляется о моем ответе, а о другом — нет. В чем разница между твитами? Читайте!
Век обычной типографии
В типографии и издательском деле всегда приходилось прикладывать на удивление много физических усилий. Отдельные буквы выбирались и составлялись вместе, одна за другой, в слова, затем фразы, а затем колонки. Цвета были чернилами — их надо было смешать и подготовить. Отдельной индустрией была подготовка и нарезка бумаги.
Деревянные блоки для задания высоты строки. (увеличенная версия)
Это касалось и пробелов. Пробелы не были отсутствием атомов, это были атомы другого вида. При создании композиции страницы для печатного пресса, надо было не только положить блоки пробелов между предложениями, но и добавить в оставшееся пространство блоки свинца или дерева. Все что сейчас называется промежутком между буквами, высотой строк, внешними и внутренними отступами — все это было физическим.
Выравнивание текста влево требовало не меньше усилий, чем выравнивание по ширине, так как пробелы все равно требовалось располагать. Необходимо было учитывать каждую долю дюйма.
Цитата в середине верстки. Обратите внимание на все блоки пробелов вокруг цитаты, удерживающие ее на месте. (увеличенная версия)
Но никто не делает. Когда мы работаем с текстом, мы обычно полагаемся на браузеры, ведь это гораздо удобнее. Многие остатки традиционной типографской техники доступны сегодня и некоторые из них действительно полезны. Это история о физических пробелах в цифровом мире.
Знакомимся с пробелами
Видели ли вы когда-нибудь полную таблицу символов Unicode? Нет? Посмотрите, это завораживает. Это история нашей цивилизации, выраженная в типографике. Она может быть недостаточно упорядочена и не объяснена полностью, но в ней есть все: языки, культуры, концепции. Географические и транспортные обозначения находятся рядом с алхимическими. Эмодзи рядом со счетными палочками. Символы валют влекут к изучению мира финансов, а дополнительные технические символы — инженерии. Здесь есть неудачные эксперименты с алфавитом и такие странности как неполная неопределенность. На другой странице будут символы проигрывания со старых видеомагнитофонов и рисунок снеговика.
И, конечно, история типографского дела здесь также сполна представлена. Вы можете путешествовать назад во времени с печатными орнаментами, расшифровывать загадки буквенных символов и сравнивать дюжину разновидностей тире — у каждого из которых есть свое назначение.
Пробелы также играют свою роль. Есть один основной, связанный с самой большой клавишей на клавиатуре, но есть и другие: очень короткие Hairspace и Thinspace и очень широкие En space и Em space и еще несколько промежуточных.
Вы можете использовать их также как и обычный пробел. Просто скопируйте из списка. Но зачем?
Очевидно. Пробелы разных размеров можно использовать для тонкой настройки сочетания элементов. Например, medium использует hair space (самый тонкий пробел, равный по ширине самой узкой шпации) для обертывания длинных тире, чтобы они не касались соседних букв:
Длинные тире в окружении очень узких пробелов на сайте Medium. (увеличенная версия)
То же самое мы делаем в письмах, в которых используется среднее тире для указания диапазона. Без узких пробелов оно будет выглядеть зажатым (а с обычным пробелом слишком свободным).
Очень узкие пробелы используются при указании диапазона на Medium. (увеличенная версия)
Точно также, если элемент меню содержит слэш, мы обертываем его узкими пробелами для лучшего баланса:
Слэш и узкие пробелы в меню на Medium. (увеличенная версия)
И так далее. Многие пробелы названы исходя из их ширины (шириной в волос, узкий, Н и М пробелы), но у некоторых название основано на их назначении. Пунктуационный пробел призван занимать столько же места, сколько и знаки пунктуации, точно также названы идеографический и математический пробелы.
Вы можете сказать, что это не круто. В конце концов, того же эффекта можно достигнуть путем обертывания элементов в и применения горизонтального пэддинга, или путем изменения свойства word-spacing и использования обычных пробелов.
Проблема этих решений в том, что они являются более громоздкими. Использование разных пробелов Юникода работает везде, не только в HTML, но и в кнопках, лейблах, полях ввода текста и заголовках E-mail. Пробелы в Юникоде очень гибкие.
Пробелы, остающиеся на месте
Теперь мы перейдем к еще трем пробелам с магическими свойствами:
Все эти пробелы ведут себя так, как будто их приклеили к соседним символам. Это значит в первую очередь то, что при переносе на новую линию слова, скрепленные такими пробелами останутся вместе. Это полезно, если вы хотите предотвратить разделение слов или символов, которые могут смотреться нелепо оказавшись на разных строках, брошенные и без присмотра (в типографии их называют сиротами).
Текст с обычными и неразрывными пробелами. (увеличенная версия)
Точно также в французском языке принято отделять завершающий знак пунктуации в предложении узким пробелом. Этот пробел тоже должен быть неразрывным, чтобы знак вопроса или кавычка были привязаны к своим словам.
Та же техника применима к длинным числам, разделенным на блоки по три цифры, телефонным номерам и прочим вещам, которые по смыслу должны находиться в одном месте.
Опять-таки, вы можете делать все это, оборачивая не разбиваемые сочетания древним тегом или span с применением свойства white-space в CSS. Но также как и в предыдущем случае, использование нужного символа, соответствующего контексту будет решением более простым и работающим независимо от разметки.
Еще один момент: несмотря на невидимость, неразрывные пробелы сохраняют свои размеры — и ширину, и высоту. Иногда это помогает правильно задать размер их контейнеру. Некоторые из вас помнят темные времена табличной верстки, когда использование неразрывного пробела помогало обеспечить видимость ячеек таблицы. Это был хак, основанный на другом хаке. Сегодня у нас есть лучшие способы для верстки макетов. Но даже сейчас, пару месяцев назад, я использовал неразрывный пробел при разработке для IOS с той же целью — он был в поле пользовательского ввода и без него высота поля была недостаточной.
Не отбрасывайте вчерашние хаки и знания. Иногда они могут пригодиться и в современных условиях. 🙂
Невидимые, но не совсем
Теперь настало время перейти к самой любопытной разновидности пробелов — к тем, у которых нет размеров совсем.
Да, он где-то здесь. Скопируйте и вставьте фрагмент целиком и удалите символы вокруг него. Вы найдете невидимый пробел, если будете проводить по фрагменту стрелками на клавиатуре — вы заметите остановку, в месте нахождения невидимого пробела.
У него нет никакой ширины, это пробел для современной цифровой эры. Но какое может быть у применение у пробела, которого нет? Целых два:
В первом случае пробел нулевой ширины работает как разбиватель слов ( ) там, где HTML недоступен. В таком случае это абсолютный антагонист неразрывного пробела. Вот пример, где он позволяет разбивать слова, разделенные слэшем:
Пробел нулевой ширины помогает разбить слова, разделенные слэшем. (увеличенная версия)
Что касается другого применения… Помните пример в самом верху? Это был пробел нулевой ширины, который предотвратил создание ссылки в моем твите. Он расположен сразу после @ и это он помешал парсеру, ищущему цифры и буквы и прекращающему поиск, при обнаружении других символов.
Два твита с нулевым пробелом. (увеличенная версия)
Можно найти и другие применения:
Существуют как творческие, так и хитрые использования невидимого пробела и надо учитывать, что некоторые парсеры умнее других. Но при разумном использовании это просто еще один инструмент для управления парсерами, когда они делают не то, что нам нужно.
А теперь все вместе
Это список всех пробелов, которые были упомянуты в статье. Вы можете скопировать этот текст и все эти пробелы будут также работать в IOS и Android.
Название пробела | HTML сущность | Код юникода |
---|---|---|
Hair space | \u200A | |
Six-per-em space | \u2006 | |
Thin space | \u2009 | |
Normal space | \u0020 | |
Four-per-em space | \u2005 | |
Mathematical space | \u205F | |
Punctuation space | \u2008 | |
Three-per-em space | \u2004 | |
En space | \u2002 | |
Ideographic space | \u3000 | |
Em space | \u2003 | |
Narrow no-break space | \u202F | |
No-break space | \u00A0 | |
Figure space | \u2007 | |
Zero-widthspace | | \u200B |
Что нужно учитывать при работе с пробелами
При более активном использовании пробелов надо держать в уме следующие пункты:
Блок с 12-пунктным пробелом из Центра книги в Сан-франциско. Да, изменение размера шрифта, требует замены блока пробела. (увеличенная версия)
Тайны Юникода
Я действительно считаю, что стоит написать хороший гайд по Юникоду. Но это уже совершенно другая история.
Текст, которого нет
Текстовые редакторы, основная задача которых — отображение моноширинного шрифта (например, кода), должны, как и следует из названия, показывать символы одной ширины.
Но есть нюанс
В Unicode есть символы, видеть которые не положено. Текстовый редактор может просто отрендерить текст с таким символом, а может предпринять какие-то действия, чтобы сделать его заметным.
Код | Пример | Название |
---|---|---|
U+2060 | foobar | WORD JOINER |
U+2061 | foobar | FUNCTION APPLICATION |
U+2062 | foobar | INVISIBLE TIMES |
U+2063 | foobar | INVISIBLE SEPARATOR |
U+180E | foobar | MONGOLIAN VOWEL SEPARATOR |
U+200B | foobar | ZERO WIDTH SPACE |
U+200C | foobar | ZERO WIDTH NON-JOINER |
U+200D | foobar | ZERO WIDTH JOINER |
U+FEFF | foobar | ZERO WIDTH NO-BREAK SPACE |
Word joiner, U+2060
Пришёл на смену zero-width no-break space (U+FEFF), потому что U+FEFF стал использоваться для кодирования BOM (byte-order mark, несколько байт в начале файла, обозначающие его кодировку и порядок байт). Этот символ запрещает перенос строки там, где он встречается.
Zero-width no-break space, U+FEFF
Устаревший символ, заменён на word joiner, использовался в тех же целях.
Zero-width joiner, U+200D
Используется в индийских и арабских шрифтах для объединения символов, которые без него не были бы соединены.
Zero-width non-joiner, U+200C
В начертаниях с лигатурами можно вставить его между буквами, чтобы лигатуры не было:
Он встречается даже на клавиатурах:
Zero-width space, U+200B
Используется, когда нужно обозначить границу слов, не вставляя пробел. Этот текст будет переноситься по словам:
Invisible Operators: function application U+2061, invisible times U+2062, invisible separator U+2063
«Невидимые операторы», добавленные в Unicode 3.2. Нужны для обозначения математических операций в выражениях.
Например, эта запись: Aij
Может означать или индекс (i, j) в двумерном массиве, или индекс i*j в одномерном. Для устранения неоднозначности можно использовать или Invisible times, или Invisible separator, чтобы было понятно, что имелось в виду.
Аналогично, f (x + y), это или умножение, или функция.
Визуально они не должны отличаться, но некоторые парсеры смогут понять, что имелось в виду.
Mongolian vowel separator, U+180E
Из названия понятно, для чего он. Этот символ уже не раз вызывал проблемы. Очень хорошо описан в этом ответе.
Как это выглядит
Конечно же, отображение зависит не только от редактора, но ещё и от шрифта, посмотрим на рендеринг текста, не меняя настроек редакторов.
Atom, Sublime, VSCode, Xamarin Studio, XCode, Notepad++:
Cat не показывает их:
Vim тоже не сообщает о некоторых символах, даже с включённой настройкой set list, а вот less справляется лучше:
GitHub, вот так показываются эти символы в pull request-ах и diff-ах:
Один из популярных редакторов кода, CodeMirror:
В том же CodeMirror, используемом jsbin, в IE часть символов видна:
ACE догадывается, что там бяка, и говорит, что что-то тут нечисто, но вот что именно — показывает не всегда:
Редакторы кода и diff tools
Редакторы на платформе IntelliJ:
Разные инструменты сравнения кода под macOS (P4Merge, FileMerge, KDiff3):
KDiff3, попытка засчитана, но этого не достаточно.
SourceTree: не обрабатывает текст вообще никак, плохо:
Tortoise, тоже почти ничего:
git diff : молодец, показал всё, ещё и выделил (хотя, на самом деле, сделал это less). Просто прекрасно, для diff tools это образец для подражания:
Anguish: brainfuck, которого нет
Кто-то сделал язык программирования Anguish, использующий только невидимые символы. Он основан на brainfuck, но использует не знаки пунктуации, а символы, о которых мы говорили выше. Есть даже интерпретатор на Perl и примеры использования.
Эксплуатация
Плохой код, фу таким быть, сделать закладку можно совсем просто:
Что делать
Пиши чистый код, %username%. Следуй best practices, их придумали не просто так, а для того чтобы держать меньше вещей в голове, в том числе своевременно замечая такие штуки. Увидел магическую строчку, странный или непроверяемый default case, ещё что-то: есть время — не поленись, перепиши как надо. Проводи код-ревью, смотри что коммитят в твою репу, поддерживай хорошее покрытие. Помни, что строке может быть не только то, что видно на экране, проверь в hex-редакторе, если возникло подозрение.
Вообще, вероятность реализации бэкдора через невидимый символ, конечно, есть, но скорее нет, чем да: найти его достаточно просто, а вставить закладку в говнокод можно и другими методами.
Кирилл Беляев
Чтобы контролировать внешний вид и поведение текста, обычного пробела не хватает. Например, отделяю пробелом в шестую часть кегля единицы измерения от значений. Или склеиваю предлоги с последующими словами. С раскладкой Бирмана неразрывный пробел вводить легко, но с пробелами, меньше обычного, она не помогает. Поэтому копировал их со страницы «Все о пробелах». Там вконце — таблица. Но всего нужного там нет. Из символов нулевой ширины есть только третий:
Название | Юникод | ХТМЛ | Мнемоника | |
1 | Zero Width No-break Space | U+FEFF | & #65279; | |
2 | Word Joiner | U+2060 | & #8288; | & NoBreak; |
3 | Zero Width Space | U+200B | & #8203; | & NegativeMediumSpace; |
4 | Zero Width Joiner | U+200D | & #8205; | & zwj; |
5 | Zero Width Non-joiner | U+200C | & #8204; | & zwnj; |
Не все эти символы — пробелы. Работают они похожим образом, поэтому их можно перепутать.
В свежем стандарте Юникода (3.2) «Word Joiner» заменил «Zero Width No-break Space», и о последнем можно забыть, несмотря на стройность названия. «Word Joiner» — непробел, но как и пробел определяет границы слов. Это влияет на поиск, парсинг, сортировку и прочее. Символ подходит для склейки конструкций вроде «км/ч» (км & NoBreak; / & NoBreak; ч).
«Zero Width Space» похож на «Word Joiner», но не склеивает слова и растягиваться при выключке по ширине, как прочие пробелы.
Управление внутри слова поручают «Zero Width Non-joiner» и «Zero Width Joiner». Например, чтобы отменить или задать лигатуру (конечно, при наличии нужных символов в шрифте). Эти символы используют в арабской, индийской, тайской, корейской и прочих письменностях без пробелов.
Zero-Width Space
Take your JavaScript to the next level at Frontend Masters.
The name zero-width space is antithetical, but it’s not without uses. In text, maybe you’d use it around slashes because you want to be sure the words are treated individually but not have any physical space around the slash:
That’s an image. WordPress was being weird about it and not escaping it even when in a code block.
That’s pretty theoretical though—I’ve never once needed to do that. It might be useful in a long word to suggest that it can be broken there… but that’s also rare as we have the soft-hyphen ( ) which is designed for that and leaves a typically appropriate hyphen at the break.
What I have needed to do is exactly the opposite: trick a system into thinking a single word is two words. Like on Twitter, if I @username or #hashtag in the text of a tweet, those will be linked up respectively. But I don’t always want that. On CSS Twitter, I might want to refer to a @media query or show an #id-selector. Toss a zero-width space between the symbols and the text and I’m all set.
Get a zero-width space on your clipboard
Here’s a Pen I created ages ago that will help you do that:
There is also a quick trick for doing it from the browser console:
And for yet another way that may appeal to you, a bookmarklet!
Copy & Paste concern
The danger with the zero-width space is, well, you can’t see it. If someone were to, for example, copy your @media query using the zero-width space trick from a tweet, it won’t work in their code editor (because it will invalidate the rule) and it might be extremely confusing. For that reason, it’s probably good to avoid using it in anything that might be copied as a code example, but probably fine when explicitly trying to not autolink something.
What’s the difference between the zero width non-joiner and the zero width space?
February 14th, 2018
In Break it up, you two!: The zero width non-joiner I discussed the purpose of the zero width non-joiner, which is to request that two adjacent characters be rendered without a ligature. Conversely, the zero width joiner requests that they be rendered with a ligature. Of course, it is up to the rendering engine to make the final decision as to how the rendering is done, but at least you can make your intentions clear.
Another character that seems very similar to the zero width non-joiner is the zero width space. Both of them have no width, and both of them break up ligatures. So what’s the difference?
Well, one of them is a space, and the other one isn’t.
The zero width space is used to indicate where one word ends and another word begins, even though there should not be any space rendered at the word boundary. This is significant for languages which have the concept of words, but not of spaces. For example, the Thai and Korean languages use multiple characters to represent words, but traditionally do not insert spaces between words. The words just run together, and readers are expected to use their experience with the language to know where one word ends and the next begins.
(This sounds kind of unfair to people who are still learning the language, but learning a language is already unfair. After all, in normal speech, people typically do not make discernable pauses between words. All the words run together, and you are expected to use your experience with the language to know where one word ends and the next begins.)
Zero-Width Spaces and Joiner Characters. The zero-width spaces are not to be confused with the zero-width joiner characters. U+200C zero width non-joiner and U+200D zero width joiner have no effect on word or line break boundaries, and zero width no-break space and zero width space have no effect on joining or linking behavior. The zero-width joiner characters should be ignored when determining word or line break boundaries.
In English, these special characters don’t have much use. You could use the zero width non-joiner to break up a ligature, say to break up the “fl” ligature in the word “wolflike”, but there’s usually not much call for it in English.
Note that if you had used a zero width space instead of a zero width non-joiner, then you are telling the layout engine that “wolf” and “like” are two separate words, that happen to run together without a space. This means that it is possible for a line break to be inserted between them.