HTML: Предотвратить разрывы строк перед некоторыми символами пунктуации, начинающимися с пробела - PullRequest
2 голосов
/ 06 октября 2019

Во французских типографских соглашениях нескольким знакам препинания, таким как ;, : или ?, должен предшествовать пробел. Это вызывает нежелательные разрывы строк, когда точка находится на границе своего контейнера HTML.

Voulez-vous coucher avec moi ce soir ? Non, merci.

становится

Voulez-vous coucher avec moi ce soir 
? Non, merci.

Цель состоит в том, чтобы получить его следующим образом:

Voulez-vous coucher avec moi ce 
soir ? Oui, bien sûr.

Я не нашел способа избежать разрывов строк перед этими символами, использующими CSS. Более того:

  • Ручное добавление   перед каждым событием не является вариантом из-за существующих данных и ограничений эргономики.
  • Использование CSS whitespace: nowrap; не вариант, потому что это должно произойтиво всех других случаях.

Я подумываю использовать глобальную функцию javascript, запускающую str.replace(" ?", " ?");, но не могу понять несколько вещей:

  • Как и когдаего запуск
  • Как выбрать соответствующие теги
  • Как заменить только innerText (т.е. не в свойствах содержимого)

Любой совет? Спасибо.

Примечание: Пробелы перед некоторыми знаками препинания на французском языке: есть ли способ CSS, чтобы избежать разрывов строк? разделяет ту же цель, но имеет чистыйОграничение CSS, которого у меня нет.

1 Ответ

3 голосов
/ 06 октября 2019

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

В основном эта проблема возникает в текстовых узлах, например:

<p>Voulez-vous coucher avec moi ce soir ? Non, merci.</p>

Это элемент (элемент p) с одним узлом Text внутри него.

возможно иметь его там, где пробел находится в одном узле Text, а пунктуация в другом:

<p><span>Voulez-vous coucher avec moi ce soir </span>? Non, merci.</p>

Там "Voulez-vous coucher avec moi ce soir" и пробел находятся в одном текстовом узле (внутри span), но пунктуация находится в другом текстовом узле (в p). Я собираюсь предположить, что это достаточно редко, нам не нужно об этом беспокоиться.

Работать с ним в текстовых узлах относительно просто:

fixSpaces(document.body);

function fixSpaces(node) {
    switch (node.nodeType) {
        case 1: // Element
            for (let child = node.firstChild;
                 child;
                 child = child.nextSibling) {
                fixSpaces(child);
            }
            break;

        case 3: // Text node
            node.nodeValue = node.nodeValue.replace(/ ([;?!])/g, "\u00a0$1");
            break;
    }
}

Live Пример:

// Obviously, you wouldn't have this in a `setTimeout` in your
// real code, you'd call it directly right away
// as shown in the answer
console.log("Before...");
setTimeout(() => {
    fixSpaces(document.body);
    console.log("After");
}, 1000);

function fixSpaces(node) {
    switch (node.nodeType) {
        case 1: // Element
            for (let child = node.firstChild;
                 child;
                 child = child.nextSibling) {
                fixSpaces(child);
            }
            break;

        case 3: // Text node
            node.nodeValue = node.nodeValue.replace(/ ([;?!])/g, "\u00a0$1");
            break;
    }
}
p {
    font-size: 14px;
    display: inline-block;
    width: 220px;
    border: 1px solid #eee;
}
<p>Voulez-vous coucher avec moi ce soir ? Non, merci.</p>

Это будет в теге script в конце body, непосредственно перед закрывающим элементом </body>.

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


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

<p><span>Voulez-vous coucher avec moi ce soir </span>? Non, merci.</p>

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

fixSpaces(document.body);

function gatherText(node, array = []) {
    switch (node.nodeType) {
        case 1: // Element
            for (let child = node.firstChild;
                 child;
                 child = child.nextSibling) {
                array = gatherText(child, array);
            }
            break;

        case 3: // Text node
            array.push(node);
            break;
    }
    return array;
}

function fixSpaces(node) {
    const texts = gatherText(node);
    for (let i = 0, len = texts.length; i < len; ++i) {
        const text = texts[i];
        const str = text.nodeValue = text.nodeValue.replace(/ ([;?|])/g, "\u00A0$1");
        if (i < len - 1 && str[str.length - 1] === " " && /^[;?!]/.test(texts[i + 1].nodeValue)) {
            // This node ends with a space and the next starts with punctuation,
            // replace the space with a hard space
            text.nodeValue = str.substring(0, str.length - 1) + "\u00A0";
        }
    }
}

console.log("Before...");
setTimeout(() => {
    fixSpaces(document.body);
    console.log("After");
}, 1000);

function gatherText(node, array = []) {
    switch (node.nodeType) {
        case 1: // Element
            for (let child = node.firstChild;
                 child;
                 child = child.nextSibling) {
                array = gatherText(child, array);
            }
            break;

        case 3: // Text node
            array.push(node);
            break;
    }
    return array;
}

function fixSpaces(node) {
    const texts = gatherText(node);
    for (let i = 0, len = texts.length; i < len; ++i) {
        const text = texts[i];
        const str = text.nodeValue = text.nodeValue.replace(/ ([;?|])/g, "\u00A0$1");
        if (i < len - 1 && str[str.length - 1] === " " && /^[;?!]/.test(texts[i + 1].nodeValue)) {
            // This node ends with a space and the next starts with punctuation,
            // replace the space with a hard space
            text.nodeValue = str.substring(0, str.length - 1) + "\u00A0";
        }
    }
}
p {
    font-size: 14px;
    display: inline-block;
    width: 220px;
    border: 1px solid #eee;
}
<p><span>Voulez-vous coucher avec moi ce soir </span>? Non, merci.</p>

Соотнесение ваших вопросов с приведенным выше:

Как и при его запуске

Помещение его в элемент script в конце body запускает его очень рано, но после того, как контент находится в DOM и, таким образом, готов к работе с ним.

Как выбрать соответствующие теги

Мы игнорируем теги и работаем на уровне текстового узла.

Как заменить только innerText (т.е. несвойства содержимого)

Работая на этом уровне текстового узла. Атрибуты не являются текстовыми узлами, поэтому мы не обрабатываем их.

...