Коротко — чисто CSS-решения, увы, не дают надёжного кросс‑браузерного способа «вырезать» часть текста из обычного выделения, если по ней перетаскивают курсор. Свойство user-select: none предотвращает выделение внутри элемента при обычном клике, но при протягивании через элемент механизм выделения всё равно может включить его содержимое.
Практичные подходы (по убыванию простоты/надёжности):
1) Фильтрация содержимого при копировании (рекомендуемая, самая простая и надёжная)
- Перехватываете событие copy, клонируете выбранный фрагмент, удаляете из клона узлы, которые не хотите копировать, и кладёте подменённый текст в буфер. Внешний вид выделения при этом останется прежним, но в буфере ненужного текста не будет.
Пример:
```js
document.addEventListener('copy', function(e){
const sel = document.getSelection();
if (!sel.rangeCount) return;
const frag = sel.getRangeAt(0).cloneContents();
// удалить элементы с классом .test
frag.querySelectorAll && frag.querySelectorAll('.test').forEach(n => n.remove());
const div = document.createElement('div');
div.appendChild(frag);
e.clipboardData.setData('text/plain', div.innerText);
e.clipboardData.setData('text/html', div.innerHTML);
e.preventDefault(); // важен, чтобы браузер не положил оригинальную версию
});
```
Плюсы: надёжно для всех браузеров; легко поддерживать и расширять. Минусы: выделение визуально остаётся, но в буфере уже нет нежелательного текста.
2) Отрисовать «букву» не как текстовый узел, а как визуальный слой (CSS-псевдоэлемент, абсолютное позиционирование, фон/картинка, canvas и т.п.)
- Убираете реальный текстовый узел из документа (или оставляете пустой placeholder), а сами символы рисуете через ::before / background / absolutely positioned элемент. Так как в DOM не остаётся текстового узла с самой буквой, стандартное выделение не сможет её захватить.
Пример идеи:
HTML:
<span class="ghost" aria-hidden="true" data-char="L"></span>
CSS:
.ghost{ display:inline-block; width:0.6ch; height:1em; position:relative; }
.ghost::before{
content: attr(data-char);
position:absolute; left:0; top:0;
pointer-events:none; user-select:none;
}
Плюсы: выделение физически не может включить символ. Минусы: надо удерживать точную позицию/ширину, сложнее при переносе/автоматическом переносе строк, доступности (нужно aria-hidden и альтернативы) и вёрстке.
3) Управлять selection на лету (selectionchange / mouseup) — очень сложно и ненадёжно
- Теоретически можно при изменении выделения смотреть, попадает ли в неё запрещённый элемент, и корректировать диапазоны. На практике управление диапазонами при промежуточных разделениях (когда нужен «разрыв» в выделении) работает по-разному в браузерах (многие не поддерживают мульти‑range), поэтому этот путь редко даёт стабильный UX.
Резюме и рекомендация:
- Если вам важно, чтобы куски текста просто не попадали в буфер — используйте обработчик copy и удаляйте эти фрагменты из клонированного содержимого (вариант 1).
- Если вам нужно полностью исключить символы из визуального выделения — рисуйте их не как текстовые узлы, а как внешний слой/псевдоэлемент/картинку (вариант 2), но будьте готовы к правкам layout и доступности.
Если хотите, могу показать конкретный пример для вашей разметки (как заменить текст на псевдоэлемент с правильным резервированием места, или расширенный copy‑фильтр, который оставляет форматирование).