JS регулярные выражение как отформатировать правильно html?

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

Есть на входе:

<div class="dialog__row_1">{FFFFFF}Склад:{33CCCC}%d {FFFFFF}из {33CCCC}%d</div>
<div class="dialog__row_2">Продуктов:%s</div>
<div class="dialog__row_2">{FFFFFF}Введите сколько желаете заказать:</div>
  1. Нужно, чтобы регулярное выражение, заменила {FFFFFF} на
<span style="color:#FFFFFF">

а закрывающий

</span>

выставить перед следующим {33CCCC}, и так пройтись по всем цветам.

  1. Теперь самое сложное, у dialog__row_1 последний цвет {33CCCC} и должно получится
<span style="color:#33CCCC">%d</span>

Но у dialog__row_2 не указал цвет в {}, поэтому надо продублировать последний цвет из dialog__row_1

Как должно быть в финале:

<div class="dialog__row_1">
<span style="color:#FFFFFF">Склад: </span>
<span style="color:#33CCCC">%d </span> 
<span style="color:#FFFFFF">из </span> 
<span style="color:#33CCCC">%d</span> // Пункт 1
</div>
<div class="dialog__row_2">
<span style="color:#33CCCC">Продуктов: %s</span> // Цвет взят из пункт 1
</div>
<div class="dialog__row_2">
<span style="color:#FFFFFF">Введите сколько желаете заказать:</span>
</div>

Ответы (2 шт):

Автор решения: vsemozhebuty

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

function colorize(match) {
  const color = match.slice(1, -1);
  const replacement =
    `${colorize.isTextChanged ? '</span>' : ''}<span style="color:#${color}">`;
  colorize.lastColor = color;
  colorize.isTextChanged = true;
  return replacement;
}

colorize.lastColor = null;
colorize.isTextChanged = false;

const divs = document.querySelectorAll('div[class^="dialog__row_"]');

for (const div of divs) {
  const text = div.textContent;

  if (!/\{[0-9A-F]{6}\}/u.test(text)) {
    if (colorize.lastColor)  {
      div.innerHTML = `<span style="color:#${colorize.lastColor}">${text}</span>`;
    }
  } else {
    div.innerHTML = `${text.replace(/\{[0-9A-F]{6}\}/gu, colorize)}</span>`;
  }

  colorize.isTextChanged = false;
}

console.log(document.querySelector('#main').innerHTML);
<div id="main">
  <div class="dialog__row_1">{FFFFFF}Склад:{33CCCC}%d {FFFFFF}из {33CCCC}%d</div>
  <div class="dialog__row_2">Продуктов:%s</div>
  <div class="dialog__row_2">{FFFFFF}Введите сколько желаете заказать:</div>
</div>

Если заменяете на стороне сервера, стоит использовать сначала что-нибудь для парсинга HTML и потом применить тот же подход.

→ Ссылка
Автор решения: Alexander Lonberg

Прежде разбиваем строку:

// Из этого '{FFFFFF}Склад:{33CCCC}%d {FFFFFF}из {33CCCC}%d'
// получаем такой массив
['{FFFFFF}Склад:', '{33CCCC}%d ','{FFFFFF}из ', '{33CCCC}%d']
// ... а из этого 'Продуктов:%s', останется как есть
['Продуктов:%s']

Далее легко извлекается цвет и оборачивается span-ном. Сохраняем последний извлеченный цвет. Какой либо цвет стоит установить по умолчанию, на случай первой строки без {ffffff}.

const reSplit = /(?=\{[0-9A-F]{6}\})/ig
const reColor = /\{[0-9A-F]{6}\}/i
let lastColor = 'FFFFFF' // default Color
const wrap = (str) => `<span style="color:#${lastColor}">${str}</span>`

function colorize(str) {
  return str.split(reSplit)
    .map((str) => {
      // Если это цвет
      const match = str.match(reColor)
      if (match) {
        // Извлекаем цвет ... сохраняем ... и удаляем его из строки
        lastColor = match[0].replace(/[\{\}]/g, '')
        return wrap(str.replace(reColor, ''))
      }
      // Иначе унаследовать
      return wrap(str)
    })
    .join('')
}

const rows = document.querySelectorAll('div[class^="dialog__row_"]')
for (let element of rows) {
  const text = colorize(element.textContent)
  console.log(text)
  element.innerHTML = text
}
body {
  background-color: #5e5e5e;
}
<div class="dialog__row_1">{FFFFFF}Склад:{33CCCC}%d {FFFFFF}из {33CCCC}%d</div>
<div class="dialog__row_2">Продуктов:%s</div>
<div class="dialog__row_2">{FFFFFF}Введите сколько желаете заказать:</div>

→ Ссылка