Универсальный скрипт для переписывания ссылок информеров в сетке доменов

Юрий Герук 2025-11-22 68
Универсальный скрипт для переписывания ссылок информеров в сетке доменов

Содержание

    Скрипт для информеров: переписываем ссылки зеркал на текущий домен и принудительно переводим .shop на .store

    Вводная

    Когда у сайта есть сеть зеркал, внешние ссылки внутри информеров часто ведут на разные домены. В результате пользователь уходит с текущего домена на соседнее зеркало. Ниже готовое решение для блока информеров. Скрипт находит ссылки внутри контейнеров с классом informer, распознает домены из сети и переписывает их. Общая логика простая. Любая ссылка на домен вида videohub.shop переводится на https://videohub.store. Любая ссылка на любое другое зеркало из списка становится относительной и открывается на том домене, где пользователь находится сейчас. Для примеров используются вымышленные домены.

    Описание задачи

    1. Нужно обработать только информеры. Поиск в документе идет внутри тегов с классом informer.
    2. Внутри информера обрабатываются два источника ссылок.
      1. Атрибут href у тега a.
      2. Атрибут data-link у любых элементов.
    3. Ссылки на зеркала должны вести либо на текущий домен, либо на основной домен по спецправилу для .shop.

    Готовый скрипт

    Вставьте этот код в шаблон перед закрывающим </body> или после информеров. Домены вымышленные. При необходимости замените на свои.

    <script>
    (function () {
     var INFORMER_SELECTOR = '.informer';
    
     // список всех зеркал проекта, примеры фейковых доменов
     var DOMAINS = new Set([
     'videohub.guru','videohub.media','videohub.bar','videohub.help','videohub.online',
     'videohub.quest','videohub.info','videohub.pics','videohub.store','videohub.hair',
     'videohub.rest','videohub.today','videohub.digital','videohub.blog','videohub.city',
     'videohub.ink','videohub.press','videohub.ru','videohub.skin','videohub.su','videohub.makeup',
     'videohub.shop','videohub.space','videohub.lol'
     ]);
    
     function normHost(h) { return String(h || '').replace(/^www\./i, '').toLowerCase(); }
     var currentHost = normHost(location.hostname);
    
     function toAbsolute(url) {
     if (typeof url !== 'string') return url;
     if (/^\/\//.test(url)) return location.protocol + url; // поддержка ссылок вида //host/path
     return url;
     }
    
     // основная функция переписывания
     function rewrite(url) {
     try {
     var abs = toAbsolute(url);
     var u = new URL(abs, location.origin);
     var host = normHost(u.hostname);
    
     // спецправило: любой videohub.shop переводим на https://videohub.store
     if (host === 'videohub.shop') {
     return 'https://videohub.store' + u.pathname + u.search + u.hash;
     }
    
     // общее правило: если ссылка на любое зеркало и это не текущий хост, делаем относительный путь
     if (DOMAINS.has(host) && host !== currentHost) {
     return u.pathname + u.search + u.hash;
     }
     } catch (e) {}
     return url;
     }
    
     function processEl(el) {
     if (el.hasAttribute('href')) {
     var href = el.getAttribute('href');
     if (/^(https?:)?\/\//i.test(href)) {
     var newHref = rewrite(href);
     if (newHref !== href) el.setAttribute('href', newHref);
     }
     }
     if (el.hasAttribute('data-link')) {
     var dl = el.getAttribute('data-link');
     if (/^(https?:)?\/\//i.test(dl)) {
     var newDl = rewrite(dl);
     if (newDl !== dl) el.setAttribute('data-link', newDl);
     }
     }
     }
    
     function processRoot(root) {
     root.querySelectorAll('a[href], [data-link]').forEach(processEl);
     }
    
     function initForNode(node) {
     if (!(node instanceof Element)) return;
     if (node.matches(INFORMER_SELECTOR)) processRoot(node);
     node.querySelectorAll(INFORMER_SELECTOR).forEach(processRoot);
     }
    
     function nearestWithAttr(start, attrs) {
     var el = start;
     while (el && el !== document && el.nodeType === 1) {
     for (var i = 0; i < attrs.length; i++) {
     if (el.hasAttribute(attrs[i])) return { el: el, attr: attrs[i] };
     }
     el = el.parentElement;
     }
     return null;
     }
    
     document.addEventListener('DOMContentLoaded', function () {
     // первичная обработка всех информеров
     document.querySelectorAll(INFORMER_SELECTOR).forEach(processRoot);
    
     // наблюдатель за динамическими изменениями
     var mo = new MutationObserver(function (muts) {
     muts.forEach(function (m) {
     m.addedNodes.forEach(initForNode);
     if (m.type === 'attributes' && (m.attributeName === 'href' || m.attributeName === 'data-link')) {
     var t = m.target;
     if (t.closest && t.closest(INFORMER_SELECTOR)) processEl(t);
     }
     });
     });
    
     mo.observe(document.body, {
     subtree: true,
     childList: true,
     attributes: true,
     attributeFilter: ['href', 'data-link']
     });
    
     // страховка на клик, чтобы переписать ссылку до навигации
     document.addEventListener('click', function (evt) {
     var container = evt.target.closest && evt.target.closest(INFORMER_SELECTOR);
     if (!container) return;
    
     var hit = nearestWithAttr(evt.target, ['href', 'data-link']);
     if (!hit) return;
    
     var val = hit.el.getAttribute(hit.attr) || '';
     if (!/^(https?:)?\/\//i.test(val)) return;
    
     var rewritten = rewrite(val);
     if (rewritten !== val) {
     hit.el.setAttribute(hit.attr, rewritten);
    
     // поддержка сценариев, где используется data-link вместо обычной навигации
     if (hit.attr === 'data-link') {
     evt.preventDefault();
     var targetAttr = hit.el.getAttribute('target');
     if (targetAttr === '_blank') {
     window.open(rewritten, '_blank', 'noopener,noreferrer');
     } else {
     location.assign(rewritten);
     }
     }
     }
     }, true); // capture, чтобы сработать раньше
     });
    })();
    </script>
    

    Как это работает по шагам

    1. Нахождение зоны работы. Скрипт ограничивает область поиска ссылок только контейнерами с классом informer. Это защищает остальные части сайта и исключает неожиданные переписывания.
    2. Объединение зеркал в список. В константе DOMAINS лежит перечень всех доменов проекта. Это быстрая проверка того, считается ли хост своим.
    3. Нормализация хоста. Функция normHost убирает префикс www. и переводит домен к нижнему регистру. Это исключает ложные несоответствия.
    4. Приведение URL к абсолютному виду. Функция toAbsolute корректно обрабатывает протокольно относительные ссылки вида //host/path, чтобы new URL разобрал их без ошибок.
    5. Логика переписывания.
      1. Если хост равен videohub.shop, ссылка меняется на абсолютную и ведет на https://videohub.store с сохранением пути, параметров и якоря.
      2. Если хост входит в список зеркал и не равен текущему, ссылка заменяется на относительный путь. В результате браузер откроет тот же материал на текущем домене.
      3. Любые другие ссылки остаются без изменений.
    6. Обработка href и data-link. Функция processEl переписывает как обычные ссылки, так и пользовательские атрибуты data-link. Это важно для информеров, где вместо прямого перехода разработчик иногда слушает клик и читает data-link.
    7. Динамика DOM. MutationObserver отслеживает добавление новых узлов информеров и изменения атрибутов href и data-link, чтобы переписывать ссылки на лету.
    8. Страховка на клик. Обработчик кликов в режиме capture перехватывает событие раньше стандартной навигации. Это гарантирует, что даже если ссылка была добавлена в самый последний момент, она все равно будет переписана корректно.

    Что и зачем применяем для информеров

    1. Локализация области. Селектор .informer избавляет от лишней нагрузки и рисков на всем сайте. Информеры часто подгружаются отдельными компонентами, потому логично изолировать логику.
    2. Унификация поведения. Пользователь остается на том домене, с которого начал навигацию. Это снижает путаницу и сохраняет сессию, cookies и аналитические метки на одном хосте.
    3. Спецправило для .shop. Маркетинговые и юридические причины иногда требуют отправлять трафик строго на одно доменное имя. Правило для videohub.shop → videohub.store решает эту задачу.
    4. Поддержка атрибута data-link. Многие тематические информеры используют не прямой href, а клик по карточке. Скрипт учитывает этот сценарий.
    5. Поддержка динамики. Информеры часто рендерятся позже основной разметки. Наблюдатель изменений обеспечивает непрерывную корректировку ссылок.

    Как подключить правильно

    1. Разместите скрипт ниже разметки информеров или перед </body>.
    2. Убедитесь, что класс контейнера информера именно informer.
    3. Проверьте, что список доменов в DOMAINS соответствует вашей сетке зеркал.
    4. При необходимости адаптируйте спецправило. Менять строку в блоке условия для .shop.

    Тестирование

    1. Откройте страницу на домене videohub.media.
    2. Наведите на ссылку внутри информера, ведущую на videohub.guru. Убедитесь, что при клике открывается путь на текущем домене videohub.media.
    3. Кликните по ссылке на videohub.shop. Убедитесь, что переход идет на https://videohub.store с тем же путем и параметрами.
    4. Проверьте карточки информера, которые используют data-link. Переход должен вести по тем же правилам.
    5. Добавьте информер динамически. Скрипт должен переписать ссылки без перезагрузки.

    Полезные детали реализации

    1. Обработчик клика использует режим capture. Это дает время переписать адрес до того, как браузер начнет переход.
    2. Навигация через data-link обрабатывается вручную. Если у элемента стоит target="_blank", открывается новая вкладка с безопасными флагами noopener и noreferrer.
    3. Относительные пути сохраняют структуру. Путь, строка запроса и якорь не теряются.
    4. Нормализация доменов исключает проблемы с www. и регистром.

    Заключение

    Скрипт решает задачу стабилизации навигации внутри информеров. Он удерживает пользователя на текущем домене, а для домена .shop принудительно ведет на .store. Реализация учитывает реальные сценарии работы информеров с href и data-link, поддерживает динамическую подгрузку и перехватывает клики заранее. Доменная сетка задается одной структурой и легко расширяется, а точечные правила добавляются в одном месте.

    Оцените полезность материала!

    Лицензия: CC BY-SA 4.0

    Автор: Юрий Герук

    Комментарии