Помогите с решением вопроса, никак не получается реализовать обрезку текста по ширине родительского контейнера на определенной величине с добавлением многоточия в конце. Есть решение на CSS, но оно не кросбраузерно, также text-overflow: ellipsis также не подходит - это работает для одной строки без переносов.
Допустим есть текст, который должен обрезаться по одному символу начиная с определенной ширины блока-родителя, например после 400px. И на оборот, дополняться по одному символу назад при увеличении вплоть до 400px с исчезновением многоточия (...)
В сети есть решение с подключением JQ-плагинов, но этот вариант пе подходит, необходим нативный JS
Набросал несколько вариантов. У всех есть плюсы и минусы. Все комментарии непосредственно в коде и в блоках на предпросмотре. (Для просмотра желательно развернуть на весь экран.)
/*
Динамическая подрезка текста, на основе сравнения
высот оригинального и подставного контейнера
*/
function fCutterText() {
document.querySelectorAll('.dynamic').forEach(function(element) {
// Создаём фейковый элемент из блока (без текста)
let oFakeElem = element.cloneNode(false);
// Назначаем стили и прячем фейк
oFakeElem.style = `
height: auto;
position: absolute;
text-align-last: justify;
/*visibility: hidden;
z-index: -100;*/
width: ` + getComputedStyle(element).width;
// Добавляем фейк к документу
document.body.appendChild(oFakeElem);
// Получаем актуальную высоту основного блока
let nHeightMain = parseFloat(getComputedStyle(element).height);
// Если у блока нет свойства "fakeText", тогда...
if (!element.fakeText) {
// Добавляем это свойство и запоминаем в нём исходный текст блока
element.fakeText = element.textContent;
}
//console.log('textAlignLast',oFakeElem.style.textAlignLast);
oFakeElem.innerText = element.fakeText;
// Если высота фейка больше оригинала, тогда...
if (parseFloat(getComputedStyle(oFakeElem).height) > nHeightMain) {
finish:
// Приращиваем строку большими подстроками...
for (let i = 0; i < element.fakeText.length; i += 20) {
// Заносим постепенно текст в фейк
oFakeElem.innerText = element.fakeText.substring(0, i);
// Если высота фейка стала больше оригинала, тогда...
if (parseFloat(getComputedStyle(oFakeElem).height) > nHeightMain) {
// Посимвольно сокращаем строку в фейке
for (let d = 0; d < element.fakeText.length; d++) {
// Заносим укороченный текст в фейк
oFakeElem.innerText = element.fakeText.substring(0, i - d);
// Если высоты уравнялись, тогда...
if (parseFloat(getComputedStyle(oFakeElem).height) <= nHeightMain) {
// Отрезаем у фейкового текста ещё 3 символа (под многоточие) и добавляем "…"
oFakeElem.innerText = oFakeElem.textContent.slice(0, -3) + '…';
// Выходим из всех циклов разом
break finish;
}
}
}
}
// Копируем текст из фейка в основной блок
element.innerText = oFakeElem.textContent;
}
else {
// Копируем текст из фейка без изменений
element.innerText = element.fakeText;
}
// Удаляем фейк из документа
document.body.removeChild(oFakeElem);
});
}
fCutterText();
/*
Динамическое появление дополнительного элемента,
на основе наличия полосы прокрутки
*/
function fShowMore() {
document.querySelectorAll('.full').forEach(function(element) {
element.querySelector('a').style.display = (element.scrollHeight > element.offsetHeight) ? 'block' : 'none';
});
}
fShowMore();
/* Слушатели событий DOM (в качестве примера, для интерактивности блоков) */
var observer1 = new MutationObserver(function(mutations) {
fCutterText();
});
document.querySelectorAll('.dynamic').forEach(function(target) {
observer1.observe(target, {
attributes: true
});
});
var observer2 = new MutationObserver(function(mutations) {
fShowMore();
});
document.querySelectorAll('.full').forEach(function(target) {
observer2.observe(target, {
attributes: true
});
});
* {
box-sizing: border-box;
}
.cutter {
box-shadow: 1px 3px 9px rgba(0, 0, 0, 0.6);
font: 16px monospace;
margin: 15px;
max-width: 100%;
overflow: hidden;
padding: 0em 1em 0em 4em;
resize: horizontal;
text-align: justify;
width: 400px;
}
.singlerow {
height: 1.4em;
text-overflow: ellipsis;
white-space: nowrap;
}
.multirow {
height: 3.6em;
position: relative;
}
.pseudo::before {
background: linear-gradient(to right, rgba(255, 255, 255, 0), #fff 54%);
bottom: 0;
content: '• • •';
font: 1em sans-serif;
height: 1.3em;
line-height: 1.5em;
position: absolute;
right: 1em;
text-align: right;
width: 6em;
}
.adjacent>a {
background: linear-gradient(to right, rgba(255, 255, 255, 0), #fff 54%);
bottom: 0;
color: #aaa;
font: .8em sans-serif;
height: 1.4em;
line-height: 1.4em;
padding-right: .3em;
position: absolute;
right: 1em;
text-align: right;
text-decoration: none;
transition: all 0.5s ease-out 0.1s;
width: 13em;
}
.adjacent>a:hover {
color: #a00;
font-weight: 700;
letter-spacing: .3em;
width: 21em;
}
.dynamic,
.full {
text-align-last: justify;
}
.full>a {
background: linear-gradient(to right, rgba(255, 255, 255, 0), #fff 4%);
bottom: 0;
color: #000;
font: .8em sans-serif;
height: 1.4em;
line-height: 1.4em;
padding-right: .3em;
position: absolute;
right: 1.3em;
text-align: right;
text-decoration: none;
transition: all 0.5s ease-out 0.1s;
width: 1.3em;
display: none;
}
.detail {
height: 3.6em;
left: 1em;
position: absolute;
width: 3.6em;
}
.detail>div {
box-shadow: 1px 2px 5px 0px black;
color: rgba(255, 255, 255, 0.6);
font-family: fantasy;
font-size: 0.8em;
margin-bottom: 0.3em;
margin-top: .3em;
padding-right: .3em;
text-align: right;
text-shadow: 1px 1px 1px #fff, -1px -1px 1px #000;
}
.css {
background: linear-gradient(to right, rgba(0, 0, 255, 0), #0d73b8 100%);
}
.html {
background: linear-gradient(to right, rgba(0, 0, 255, 0), #e45125 100%);
}
.js {
background: linear-gradient(to right, rgba(0, 0, 255, 0), #e4a229 100%);
}
<hr>
<div class="detail">
<div class="css">CSS</div>
</div>
<div class="cutter singlerow">Одиночная строка, которая обрезается с помощью "text-overflow:ellipsis"</div>
<hr>
<div class="detail">
<div class="css">CSS</div>
</div>
<div class="cutter multirow pseudo">А этот текст больше и не помещается в контейнер. С помощью псевдоэлемента ::before, в правом нижнем углу, даём понять, что это не весь текст. Тут, нужно добавлять контейнеру обработчик, чтобы пользователь смог перейти к полному тексту, кликнув по контейнеру.
</div>
<hr>
<div class="detail">
<div class="css">CSS</div>
<div class="html">HTML</div>
</div>
<div class="cutter multirow adjacent">От примера выше, отличие лишь в том, что псевдоэлемент заменён на обычный тег-ссылку "A". Этот элемент уже поддаётся более продвинутой кастомизации (анимация, стилизация, события и т.д.).<a href="#">read more…</a></div>
<hr>
<div class="detail">
<div class="css">CSS</div>
<div class="js">JS</div>
</div>
<div class="cutter multirow dynamic">В этом примере, задействуется реальная подрезка, т.е. вывод только того контента, который помещается в блок. Это самый правильный метод, но к сожалению, не всегда корректно работает (или я чего-то не учёл)). Ещё один минус данного подхода - малое быстродействие.
На самом деле, кода не так уж много (большую часть занимают комментарии в коде и вспомогательные функции для наглядности).</div>
<hr>
<div class="detail">
<div class="css">CSS</div>
<div class="html">HTML</div>
<div class="js">JS</div>
</div>
<div class="cutter multirow full">Ещё одна неплохая реализация, основанная на сравнении свойств <em>scrollHeight</em> и <em>offsetHeight</em>. Довольно простая и надёжная - оптимальный вариант.<a href="#">…</a></div>
<hr>
Если интерактивность не требуется, то функции подрезки (в примерах с JS) нужно запускать там же, где что-то меняет текст в блоке. Т.е. вставили текст - запустили подрезку.
Виртуальный выделенный сервер (VDS) становится отличным выбором
При разборе мануала по использованию API от Яндекса столкнулся со следующей проблемойНепонятно откуда берется(или как/где подключается) объект...
Я создаю реакт компонент, содержащий в себе элементы, одним из которых является форма содержащая кнопкиТак как кнопки внутри формы, по-умолчанию,...
Нашел в сети готовый код на JS расчета предполагаемой даты родовС кодом все ОК, он прост и понятен