Днями виникла задача реалізувати своєрідний спойлер на jquery. Суть зводиться до того, щоб якщо в блок виводиться занадто довгий текст, наприклад, перевищує 2000 символів, тоді текст повинен обрізатися, а в кінець вставлятися крапки. До того ж після блоку необхідно виводити посилання «Читати далі», яка буде розкривати текст повністю. Слід було також не забувати і про функціонал зворотного згортання блоку в формат анонса.
Завдання досить стандартна, проте пошук відповідного jquery-плагіна не увінчався успіхом, оскільки у всіх плагінах, які вдалося знайти, обмеження задаються не кількістю символів, а висотою блоку. Тобто вказується висота контейнера, і плагін обрізає текст, що виходить за його межі. В інтернеті можна знайти дуже зручні варіанти таких плагінів, наприклад, Readmore.js і dotdotdot . Причому останній навіть може відслідковувати зміну розміру вікна і автоматично оновлювати результат.
Однак проблема була в тому, що сайт адаптивний і блок приймає різну ширину в залежності від ширини вікна браузера. У підсумку могла вийти ситуація, коли в блоці виявиться зовсім мало символів. Звичайно ж, можна по якомусь алгоритму міняти висоту блоку при ресайз вікна, проте було прийнято рішення не робити якусь надбудову над якимось готовим рішенням, а написати свій невеликий плагін, який буде виконувати обрізку тексту на jquery виходячи з заданої кількості символів.
Можна виділити кілька переваг даного підходу.
- Можна бути впевненим, що в блоці буде відображатися обсяг тексту, який несе смислове навантаження навіть в згорнутому стані.
- Не потрібні зайві обробники на зміну розміру вікна, які б постійно виконували перевірку того, скільки тексту влазить в блок.
При реалізації також необхідно було врахувати дві речі, які впливають на красу результату. По-перше, потрібно щоб текст обрізався по цілому слову. По-друге, уникнути ситуації «розкривати занадто мало» - може статися в разі, коли загальна кількість символів в блоці трохи більше заданого значення, за яким слід проводити обрізання, наприклад, на 50-100 символів. Якщо відповідних установок буде вказані, то модуль буде використовувати дефолтні значення.
Отже, алгоритм завдання досить простий:
- Вирізаємо необхідну кількість символів - формуємо анонс.
- Доповнюємо текст анонса трьома крапками і додаємо html-обв'язку у вигляді посилання «Читати далі».
- Навішуємо обробник на посилання, яка буде змінювати текст в блоці на анонс і повний залежно від стану.
Сама трудомістка частина реалізації скрипта html-спойлера - отримання анонса з повного тексту блоку, оскільки в блоці може бути не просто текст, а відформатована за допомогою html-тегів розмітка.
Нижче приведена функція, що відповідає за вирізання з html-коду шматка, у якого кількість символів, які не є html-розміткою, так само заданому в параметрах значенням або більше на величину закінчення останнього слова анонса.
// формування анонса person.cutBrief = function () {var tmp, i = 0, // лічильник циклів j = 0, // лічильник циклів html = data.html, // html блоку htmlLength = html.length, // кількість символів html блоку count = 0, // лічильник текстових символів countFlag = true, // поточний символ не є html-розміткою endCharsLen = ENDCHARS.length, // розмір масиву символів, що вказують на закінчення слова end = htmlLength, // позиція кінця анонса при пошуку resultLimit = data.limit.total - data.limit.delta, // необхідну кількість символів tagName, // назва тега tagStack = []; // стек тегів, які необхідно закрити в кінці анонсу if (data.count> data.limit.total) {// формуємо анонс for (; i <htmlLength; i ++) {// якщо відкривається тег if (html [i] = == '<') {countFlag = false; // символ не останній if (i <htmlLength - 1) {// тег є закриває if (html [i + 1] === '/') {tmp = html.indexOf ( '>', i + 1); if (tmp> 0) {// вірний формат закриття тега tagName = html.substr (i + 2, tmp-i-2); // виявлений тег повинен мати закриває частину? if ($ .inArray (tagName, TAGDIC)> = 0) {tagStack.pop (); }}} Else {// тег є відкриває // наступний символ - будь-яка латинська буква? if (/\w/gi.test(html[i+1])) {// отримання імені тега і опреденіе його на необхідність закриття tmp = html.indexOf ( '>', i + 1); if (tmp> 0) {tagName = html.substr (i + 1, tmp-i-1); // тег повинен мати закриває частину if ($ .inArray (tagName, TAGDIC)> = 0) {tagStack.push (tagName); }} Else {// не є тегом countFlag = true; }} Else {// не є тегом countFlag = true; }}}} // інкремент лічильник текстових символів if (countFlag) {count ++; } // якщо закривається тег if (html [i] === '>') {countFlag = true; } // дійшли до кінця необхідного розміру анонса if (count> = resultLimit) {// поточний символ не є кінцем слова if ($ .inArray (html [i], ENDCHARS) <0) {// символ не останній if (i <htmlLength - 1) {// наступний символ теж не кінець слова if ($ .inArray (html [i + 1], ENDCHARS) <0) {// шукаємо перше входження кожного символу з набору і вибираємо найближчий for (; j < endCharsLen; j ++) {tmp = html.indexOf (ENDCHARS [j], i + 1); if ((tmp> 0) && (tmp <end)) {end = tmp; }}; i = end; }}} Else {// слово закінчилося цілком count--; } Break; }}; // вирізаємо шматок html data.brief = html.substr (0, i); // додаємо точки data.brief + = opt.ellipsis; // закриваємо відкриті теги for (i = tagStack.length - 1; i> = 0; i--) {data.brief + = '</' + tagStack [i] + '>'; }; } Else {// не обрізати data.brief = html; }};
Застосовується плагін стандартно:
$ ( '. B-block - first'). Readmore ();
В плагіні передбачені наступні параметри:
- ellipsis {string} - символи, які будуть виводитися в кінці анонсу;
- textOpen {string} - текст посилання в згорнутому стані;
- textClose {string} - текст посилання в розгорнутому стані;
- callback {function} - функція, що виконується після розкриття / закриття блоку;
- brief {integer} - максимальна кількість символів анонса, зменшене на величину addition;
- addition {integer} - мінімальна кількість символів розкривається частини тексту;
- smoothly {integer} - час плавного розкриття / закриття блоку в мілісекундах.
Слід зауважити, що callback-функція спрацьовує тільки після закінчення анімації тексту, яка виконується на jquery за допомогою методу animate, і приймає два вхідних параметра: посилання блок і поточний стан.
Нижче наведено приклад того, як можна в залежності від стану блоку, виконувати будь-які свої дії:
$ ( '. B-block - second'). Readmore ({ellipsis: '[...]', textOpen: 'Відкрити', textClose: 'Закрити', callback: function (self, state) {state? Self .css ( 'background', '# e74c3c'): self.css ( 'background', '# 3498db');}, brief: 500, addition: 100});
CSS в прикладі по мінімуму, можна і зовсім без нього обійтися (код наведено на SCSS):
.b-readmore {padding: 15px 0 0 0; & __ link {color: # 000; text-decoration: underline; &: Hover, &: focus, &: active {color: # 000; text-decoration: none; }} & __ open {display: inline-block; } & __ close {display: none; } & - opened & {& __ open {display: none; } & __ close {display: inline-block; }}}
Оцінюємо і коментуємо, як краще зробити спойлер для сайту за що задається кількістю символів.
Substr (i + 2, tmp-i-2); // виявлений тег повинен мати закриває частину?Pop (); }}} Else {// тег є відкриває // наступний символ - будь-яка латинська буква?
Readmore ({ellipsis: '[...]', textOpen: 'Відкрити', textClose: 'Закрити', callback: function (self, state) {state?