Содержание
Скрипт для информеров: переписываем ссылки зеркал на текущий домен и принудительно переводим .shop на .store
Вводная
Когда у сайта есть сеть зеркал, внешние ссылки внутри информеров часто ведут на разные домены. В результате пользователь уходит с текущего домена на соседнее зеркало. Ниже готовое решение для блока информеров. Скрипт находит ссылки внутри контейнеров с классом informer, распознает домены из сети и переписывает их. Общая логика простая. Любая ссылка на домен вида videohub.shop переводится на https://videohub.store. Любая ссылка на любое другое зеркало из списка становится относительной и открывается на том домене, где пользователь находится сейчас. Для примеров используются вымышленные домены.
Описание задачи
- Нужно обработать только информеры. Поиск в документе идет внутри тегов с классом informer.
- Внутри информера обрабатываются два источника ссылок.
- Атрибут href у тега a.
- Атрибут data-link у любых элементов.
- Ссылки на зеркала должны вести либо на текущий домен, либо на основной домен по спецправилу для .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>
Как это работает по шагам
- Нахождение зоны работы. Скрипт ограничивает область поиска ссылок только контейнерами с классом informer. Это защищает остальные части сайта и исключает неожиданные переписывания.
- Объединение зеркал в список. В константе DOMAINS лежит перечень всех доменов проекта. Это быстрая проверка того, считается ли хост своим.
- Нормализация хоста. Функция normHost убирает префикс www. и переводит домен к нижнему регистру. Это исключает ложные несоответствия.
- Приведение URL к абсолютному виду. Функция toAbsolute корректно обрабатывает протокольно относительные ссылки вида //host/path, чтобы new URL разобрал их без ошибок.
- Логика переписывания.
- Если хост равен videohub.shop, ссылка меняется на абсолютную и ведет на https://videohub.store с сохранением пути, параметров и якоря.
- Если хост входит в список зеркал и не равен текущему, ссылка заменяется на относительный путь. В результате браузер откроет тот же материал на текущем домене.
- Любые другие ссылки остаются без изменений.
- Обработка href и data-link. Функция processEl переписывает как обычные ссылки, так и пользовательские атрибуты data-link. Это важно для информеров, где вместо прямого перехода разработчик иногда слушает клик и читает data-link.
- Динамика DOM. MutationObserver отслеживает добавление новых узлов информеров и изменения атрибутов href и data-link, чтобы переписывать ссылки на лету.
- Страховка на клик. Обработчик кликов в режиме capture перехватывает событие раньше стандартной навигации. Это гарантирует, что даже если ссылка была добавлена в самый последний момент, она все равно будет переписана корректно.
Что и зачем применяем для информеров
- Локализация области. Селектор .informer избавляет от лишней нагрузки и рисков на всем сайте. Информеры часто подгружаются отдельными компонентами, потому логично изолировать логику.
- Унификация поведения. Пользователь остается на том домене, с которого начал навигацию. Это снижает путаницу и сохраняет сессию, cookies и аналитические метки на одном хосте.
- Спецправило для .shop. Маркетинговые и юридические причины иногда требуют отправлять трафик строго на одно доменное имя. Правило для videohub.shop → videohub.store решает эту задачу.
- Поддержка атрибута data-link. Многие тематические информеры используют не прямой href, а клик по карточке. Скрипт учитывает этот сценарий.
- Поддержка динамики. Информеры часто рендерятся позже основной разметки. Наблюдатель изменений обеспечивает непрерывную корректировку ссылок.
Как подключить правильно
- Разместите скрипт ниже разметки информеров или перед </body>.
- Убедитесь, что класс контейнера информера именно informer.
- Проверьте, что список доменов в DOMAINS соответствует вашей сетке зеркал.
- При необходимости адаптируйте спецправило. Менять строку в блоке условия для .shop.
Тестирование
- Откройте страницу на домене videohub.media.
- Наведите на ссылку внутри информера, ведущую на videohub.guru. Убедитесь, что при клике открывается путь на текущем домене videohub.media.
- Кликните по ссылке на videohub.shop. Убедитесь, что переход идет на https://videohub.store с тем же путем и параметрами.
- Проверьте карточки информера, которые используют data-link. Переход должен вести по тем же правилам.
- Добавьте информер динамически. Скрипт должен переписать ссылки без перезагрузки.
Полезные детали реализации
- Обработчик клика использует режим capture. Это дает время переписать адрес до того, как браузер начнет переход.
- Навигация через data-link обрабатывается вручную. Если у элемента стоит target="_blank", открывается новая вкладка с безопасными флагами noopener и noreferrer.
- Относительные пути сохраняют структуру. Путь, строка запроса и якорь не теряются.
- Нормализация доменов исключает проблемы с www. и регистром.
Заключение
Скрипт решает задачу стабилизации навигации внутри информеров. Он удерживает пользователя на текущем домене, а для домена .shop принудительно ведет на .store. Реализация учитывает реальные сценарии работы информеров с href и data-link, поддерживает динамическую подгрузку и перехватывает клики заранее. Доменная сетка задается одной структурой и легко расширяется, а точечные правила добавляются в одном месте.
Оцените полезность материала!
Лицензия: CC BY-SA 4.0
Автор: Юрий Герук
Комментарии