Категории форума

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

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

webitproff
webitproff • 04.05.2026 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-ссылку на товар с сохранением всех специальных символов в тексте, но с безопасным экранированием атрибутов — так, чтобы при вставке в любой редактор ссылка оставалась рабочей и корректно отображалась.

Отредактировано: webitproff (04.05.2026 03:45, 2 месяца назад)
🗿

🧙‍♂️ Радуйся, странниче добрый и честный! Не понапрасну привела тя стезя в земли сии. Зде бо мужи мудры и искусники дела писменнаго и численнаго, что куют словеса и плетут узоры кода, тайны разгадывают и знанием делятся, летописи множа и ремесло цифровое творя.
⬅️ Аще налево пойдеши — обрящеши грамоты древния, писания старинныя и свитки учения. ⬆️ Аще прямо — слово мудрое и совет от мужей разумеющих, что в деле кодописном искусны, получиши. ➡️ Аще направо — сам в писцы и летописцы внидеши, и в ремесло цифровое, где строки и знаки в дело живое обращаются, слово свое в память веков вложиши.
📝 Не медли же — нареки имя свое, и вниди в чин наш, и стань с нами в братстве, где код есть магия, а строки — заклятия живыя, числом и буквою слово едины суть.
🏰 Не тайно есть собрание сие, и не инициации в нем, но многое сокрыто от пришлых и ступалого, понеже не всякому открывается знание, но токмо ищущим и пребывающим в деле сем.