В идеале вы должны заменить пространство на жесткий диск на стороне сервера , а не на стороне клиента, поскольку, если вы сделаете это на стороне клиента, у вас возникнет проблема, заключающаяся в том, чтосодержимое обновится при обновлении, что вызовет ощутимую вспышку / движение.
В основном эта проблема возникает в текстовых узлах, например:
<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 (т.е. несвойства содержимого)
Работая на этом уровне текстового узла. Атрибуты не являются текстовыми узлами, поэтому мы не обрабатываем их.