Forum categories

Записки CotoДела

Мои записи, как типовые решения разных вопросов с Cotonti

webitproff
webitproff • 2026-05-04 03:38 #552

Заметка: кнопка «Копировать ссылку на товар»

Кнопка Копировать позволяет скопировать в буфер обмена готовую HTML-ссылку на товар (со всеми атрибутами) для последующей вставки в форум, блог или другую форму сайта.
Скопированная ссылка содержит:

  • текст — полное название товара,

  • атрибут title — рекламную фразу с призывом купить,

  • URL товара.

Благодаря экранированию специальных символов (прежде всего двойных кавычек " в атрибутах) готовая ссылка не «ломает» HTML страницы, куда её вставляют.

Ниже приведён полный код кнопки с подробными комментариями.

 

<!-- Кнопка копирования ссылки на товар -->
<button class="btn btn-primary copy-btn"
        onclick="copyProductLink(this)"
        data-title="{PHP|htmlspecialchars({PHP.item.fieldmrkt_title})}"
        data-metatitle="{PHP|htmlspecialchars({PHP.item.fieldmrkt_metatitle})}"
        data-url="{PHP.cfg.mainurl}/{PHP.item.fieldmrkt_pageurl}"
        data-bs-toggle="tooltip"
        data-bs-placement="top"
        data-bs-title="{PHP.langSkStr.tools_copy_link_title}">
    {PHP.langSkStr.tools_copy_link}
</button>

<script>
function copyProductLink(btn) {
    // Получаем данные из data-атрибутов кнопки.
    // Благодаря htmlspecialchars в шаблоне кавычки уже безопасны,
    // но в dataset браузер возвращает исходные символы.
    var title = btn.dataset.title;           // Название товара
    var metaTitle = btn.dataset.metatitle;   // Мета-тайтл (альтернативное название)
    var url = btn.dataset.url;               // URL товара
    var langString = '{PHP.langSkStr.tools_copy_preff_title}'; // Языковая строка для атрибута title

    // ------------------------------------------------------------
    // Функция экранирования значений для HTML-атрибутов.
    // Заменяет символы, способные сломать разметку атрибутов:
    //   & → &amp;   < → &lt;   > → &gt;   " → &quot;   ' → &#39;
    // Это обязательно для title и href, чтобы кавычки не «разрывали» атрибуты.
    // ------------------------------------------------------------
    function escapeAttr(str) {
        if (!str) return '';
        return str.replace(/&/g, '&amp;')
                  .replace(/</g, '&lt;')
                  .replace(/>/g, '&gt;')
                  .replace(/"/g, '&quot;')
                  .replace(/'/g, '&#39;');
    }

    // ------------------------------------------------------------
    // Если название товара пустое, формируем заглушку из URL.
    // ------------------------------------------------------------
    var plainTitle = title;
    if (!plainTitle || plainTitle.trim() === '') {
        var slug = url.split('/').pop();                     // последний сегмент URL
        plainTitle = decodeURIComponent(slug.replace(/[-_]/g, ' '));
    }
    var plainMeta = metaTitle || plainTitle;

    // ------------------------------------------------------------
    // Сборка HTML-ссылки.
    // В атрибутах title и href применяем escapeAttr(),
    // а текст ссылки НЕ экранируем — чтобы он выглядел естественно
    // (кавычки внутри текста допустимы).
    // ------------------------------------------------------------
    var html = '<a href="' + escapeAttr(url) + '" title="' + escapeAttr(langString + (metaTitle || '')) + '">' + title + '</a>';
    var plain = '<a href="' + escapeAttr(url) + '" title="' + escapeAttr(langString + plainMeta) + '">' + plainTitle + '</a>';

    // ------------------------------------------------------------
    // Копирование в буфер обмена.
    // Используем современный Clipboard API, а при его отсутствии —
    // fallback с временным textarea.
    // ------------------------------------------------------------
    if (navigator.clipboard && navigator.clipboard.write) {
        var clipboardItem = new ClipboardItem({
            'text/html': new Blob([html], { type: 'text/html' }),  // для визуальных редакторов
            'text/plain': new Blob([plain], { type: 'text/plain' }) // для блокнота / исходного кода
        });
        navigator.clipboard.write([clipboardItem]).then(success).catch(function() { fallback(plain); });
    } else {
        fallback(plain); // резервный способ
    }

    // ------------------------------------------------------------
    // Визуальная обратная связь: меняем текст и цвет кнопки на 2 секунды
    // ------------------------------------------------------------
    function success() {
        btn.innerText = '{PHP.langSkStr.tools_copy_done}';
        btn.classList.remove('btn-primary');
        btn.classList.add('btn-success');
        setTimeout(function() {
            btn.innerText = '{PHP.langSkStr.tools_copy_link}';
            btn.classList.remove('btn-success');
            btn.classList.add('btn-primary');
        }, 2000);
    }

    // ------------------------------------------------------------
    // Резервный метод копирования (для старых браузеров)
    // ------------------------------------------------------------
    function fallback(text) {
        var textarea = document.createElement('textarea');
        textarea.value = text;
        document.body.appendChild(textarea);
        textarea.select();
        try {
            document.execCommand('copy');
            success();
        } catch (err) {
            btn.innerText = 'Ошибка копирования';
        }
        document.body.removeChild(textarea);
    }
}
</script>

 

результат:

<a href="https://cotonti/market/notebokers/somealias" title="Ноутбук Lenovo IdeaPad 1 15 Cloud Grey / 15.6&quot;">Ноутбук Lenovo IdeaPad 1 15 Cloud Grey / 15.6"</a>

 

Пример работы скрипта для ноутбука с характеристиками, содержащими спецсимволы.

Модель: ASUS ZenBook 14 (Intel Core i7-1360P, 16GB RAM, 512GB SSD, 14″ 2.8K OLED).
1. HTML-версия (для визуальных редакторов, форумов и т.п.)
 

<a href="https://example.com/catalog/notebooks/asus-zenbook-14" title="Купить в Захалустье по низкой цене ASUS ZenBook 14 (Intel Core i7-1360P, 16GB RAM, 512GB SSD, 14&quot; 2.8K OLED)">ASUS ZenBook 14 (Intel Core i7-1360P, 16GB RAM, 512GB SSD, 14" 2.8K OLED)</a>

Что здесь происходит:

   В атрибуте title символ дюйма " заменён на &quot;, чтобы не «сломать» структуру HTML.

   В тексте ссылки (между >...<) двойная кавычка осталась как есть — она отображается естественно (14" 2.8K OLED) и не нарушает разметку.

2. Plain-версия (для блокнота или исходного кода)

<a href="https://example.com/catalog/notebooks/asus-zenbook-14" title="Купить в Захалустье по низкой цене ASUS ZenBook 14 (Intel Core i7-1360P, 16GB RAM, 512GB SSD, 14&quot; 2.8K OLED)">ASUS ZenBook 14 (Intel Core i7-1360P, 16GB RAM, 512GB SSD, 14" 2.8K OLED)</a>

Как выглядит при вставке на форум/сайт:

   Видимый текст ссылки:
   ASUS ZenBook 14 (Intel Core i7-1360P, 16GB RAM, 512GB SSD, 14" 2.8K OLED)

   Всплывающая подсказка (title):
   Купить в Захалустье по низкой цене ASUS ZenBook 14 (Intel Core i7-1360P, 16GB RAM, 512GB SSD, 14" 2.8K OLED)

Таким образом, ссылка полностью рабочая, корректно отображается и не содержит «битого» HTML благодаря экранированию кавычек в атрибутах.

 

Итог:
Кнопка генерирует и копирует в буфер обмена готовую HTML-ссылку на товар с сохранением всех специальных символов в тексте, но с безопасным экранированием атрибутов — так, чтобы при вставке в любой редактор ссылка оставалась рабочей и корректно отображалась.

This post was edited by webitproff (2026-05-04 03:45, 2 months ago)
🗿

🧙‍♂️ Well met, good traveler! Fate has guided your steps to our humble realm. Here, wise sages and masters of the coding craft ⚒️📜 gather to unravel the mysteries of the code ✨⚙️, share their knowledge, and add new chapters to our chronicles.
⬅️ To the left lie ancient scrolls 📚 filled with forgotten wisdom. ⬆️ Straight ahead, friendly folk offer counsel 💬 to those in need. ➡️ To the right, you may become a chronicler ✍️ yourself, sharing your craft and leaving words of wisdom for those yet to come.
📝 Delay no longer—tell us the name by which you are known, complete the rite of registration 🛡️, and join our fellowship!
🏰 Our brotherhood holds no secret initiation, yet many halls remain closed to wandering guests, for some knowledge 💎 is reserved for those who choose to walk this path beside us.