Comments 16
Можно также улучшить читаемость текста с помощью CSS-свойства text-wrap: pretty; оно правда не устоявшееся.
Была статья на хабре https://habr.com/ru/articles/899194/
Видел, но, если не ошибаюсь, вопрос с "висячими" словами же не решает?
Да-да, text-wrap
не решает задачу избавления от висячих предлогов
Решает. Другой момент, что именно вы называете висячим словом. Вижу у вас в массиве предлог "перед", это целых 5 букв. Не удивлюсь, если text-wrap: pretty не переносит слова подобных размеров и не должен
Автор этой статьи в самом начале определяет, что он имеет ввиду под "висячими словами"
> В типографике существует понятие “висячих слов” — это короткие слова (предлоги, союзы, местоимения), которые остаются в конце строки при переносе текста. Такие переносы нарушают удобочитаемость и эстетику текста. В русской типографике принято избегать переносов после коротких слов длиной 1-2 символа.
В самом коде все предлоги и союзы определяются в это понятие "висячего слова". Вот по такой формулировке text-wrap
ничего не решает.
Бобо мертва. На круглые глаза
вид горизонта действует, как нож, но
тебя, Бобо, Кики или Заза
им не заменят. Это невозможно.
Вжух — и форматирование Бродского поехало)
Итого получаем:
Перебор ВСЕГО дом-дерева в поисках указанных текстовых элементов;
Перебор получившегося массива текстовых элементов с последующим перебором текстовых элементов внутри него, т.е. цикл в цикле;
И внутри этого цикла у нас replace, внутри коллбэка которого ещё и includes по массиву, т.е. ещё несколько переборов внутри переборов.
В плане производительности получается дичь, даже с сотней-другой текстовых элементов небольшого текстового содержания. На большом проекте скорее всего может просто повесить вкладку)
Как можно сделать быстрее/лучше/проще:
Вынести текст в константы, и уже по ним устроить перебор - как минимум не придётся перебирать DOM в поисках текстовых элементов, а в элементах искать текстовые узлы. Можно отдать в них уже форматированный текст, а не наоборот. Предлоги вынести в Set вместо массива, чтобы исключить ещё один перебор.
Неразрывные пробелы нужны не во всех случаях. Я бы даже сказал что они нужны реже, чем не нужны) текстовый блок относительно короткий или нет в нём коротких предлогов. Соответственно и перебирать их просто нет нужды. Здесь можно, используя описанный выше подход, итерироваться не по всем текстовым константам, а только по тем, где вам неразрывный пробел необходим. Например собирать из них отдельный массив/объект. Но так как при таком подходе уже придётся что-то отделять самостоятельно, то:
Самое простое - в тех же текстовых константах(или в разметке на месте) ставить неразрывный пробел руками. Это делается один раз при вставке с макета, глаз обычно уже намётан. На текущем месте так и делаем, никто не пострадал.
А ещё изредка бывает так, что для мобильной вёрстки в конкретном месте перед коротким предлогом НЕ НУЖЕН неразрывный пробел, чтобы текст встал красиво. И в таком случае автоматика тоже будет мешать
Ну без тестов нельзя утверждать. У меня подобная вещь работает на странице. Текст длинной около 19К обрабатывается примерно за 110 мс.
Браузер Хром, Проц i5 12100
На мозиле немного дольше - 150 мс.
Там даже еще ставятся неразрывные пробелы нулевой длины между скобками и содержимым внутри, знаки минус или дефис между буквами заменяются на неразрывный дефис, ставится неразрывный пробел перед тире.
Благодарю за такой подробный коммент!
В самом низу статьи писал о том, что, да, бывает на мобильном переносит не так, как хотелось бы и может "уехать" за контейнер длинное слово. Скрипт не идеален, в будущем, может быть, опубликую вторую версию того, как решать вопрос с "висячими". Пока использую этот скрипт на сайтах, где это "можно позволить", на высоко-посещаемых ресурсах не внедряю.
Ну не знаю, какой длины слово должно быть. Или компоновка неудачная (несколько колонок на мобильном не делают). При минимальной ширине на мобильнике 360 (может и бывает меньше, но давно не встречались) И высоте шрифта 14px - в строке помещается 36-40 символов. Ну не знаю я таких слов. На мобильных обычно делается выравнивание по левому краю и разрешение переносов. Все более-менее получается. Лесенка, но это же смартфон. Большего от него требовать трудно.
Там у меня примерно такой код
const reprednbs = /((?<![^\s\(«'"])(с|со|см\.|в|во|вне|у|к|ко|на|не|ни|над|по|под|про|при|за|о|от|об|обо|до|для|и|из|им\.|без|а|или|\/\/)(\s+|$))/ig;
const rechastnbs = /\s+(же|ли|бы)(?=[\s\)\.,\:;»'"$])/ig;
const redefis = /([^\s\u2002])([\-\u{2010}\u{2011}\u{2012}\u{2013}\u{2014}])([^\s\u2002])/ug;
const retire = /(\s+)([-\u{2010}\u{2011}\u{2012}\u{2013}\u{2014}])(\s+)/ug;
let rep = false
let text = node.data; // node - текстовый узел
/* Ставим неразрывный пробел после предлогов и частиц*/
if (reprednbs.test(text)) {
text = text.replaceAll(reprednbs, '$2\u00a0');
rep = true;
}
/* Ставим неразрывный пробел перед частицами бы ли же*/
if (rechastnbs.test(text)) {
text = text.replaceAll(rechastnbs, '\u00a0$1');
rep = true;
}
/* Ставим неразрывный дефис*/
if (redefis.test(text)) {
text = text.replaceAll(redefis, '$1\u2011$3');
rep = true;
}
/* Ставим тире и неразрывный пробел перед ним*/
if (retire.test(text)) {
text = text.replaceAll(retire, '\u00a0\u2014 ');
rep = true;
}
//.... Еще всякие варианты
if (rep) {
node.data = text;
}
Как ни странно, с предварительной проверкой ( if (re.test()) ) работает хоть и немного (3-5%) но быстрее.
У нас там на страницах бывает много формул, которые обрабатываются МathJаx. Так он может работать 3-5 сек. Да и грузится он не быстро. Пока он грузится (async), все это успеет обработаться.
Про высокопосещаемые ресурсы - непонятно. Они дают нагрузку на сервер. А обработка идет в браузере. Ему то все равно высоко это посещаемый ресурс или нет.
Как переносить «висячие» слова на следующую строку с помощью JavaScript