Обо мне
Услуги
Я использую на сайте куки. В интернете без них никак
OK

Скрипт для отслеживания реальных кликов по номеру телефона для Яндекс Метрики

Просто вставьте номер вашего счетчика ниже и получите готовый скрипт.
Цели для Яндекс Метрики
Не забудь настроить цели, чтобы отслеживать события. Через Java-Script-событие
  • phone_click_try
    Срабатывает при попытке клика по телефонному номеру (когда пользователь кликнул на ссылку с tel: или элемент с атрибутом data-phone)
  • phone_slider_success
    Срабатывает при успешном прохождении слайдера (когда пользователь провел ползунок вправо до конца для подтверждения вызова)
  • phone_redirect
    Отправляется через 500 миллисекунд после прохождения слайдера или при закрытии страницы, подтверждая фактический переход к телефонному вызову

Генератор с вашим счетчиком

Введите номер вашего счетчика Яндекс.Метрики, и скрипт автоматически обновится:

<script>
/**
 * ============================================
 * Скрипт защиты от ботов для мессенджеров
 * Разработка: Истомин Антон
 * TG-блог: "ADSкий Яндекс Директ"
 * ============================================
 */
(function () {
  // ==== КОНФИГУРАЦИЯ ====
  var COUNTER_ID = 45588258; // ID Метрики
  var DELAY_BEFORE_REDIRECT_GOAL = 500; // уменьшено с 15000 до 500ms для надежной отправки
  var SS_PENDING_REDIRECT_KEY = 'phonePendingRedirect';
  var lastClickTimestamp = 0; // для дебаунсинга

  function unwrapRedirect(url) {
    try {
      if (!url) return url;
      if (/^tel:/i.test(url)) return url;
      var u = new URL(url, window.location.origin);

      for (var pair of u.searchParams.entries()) {
        var val = decodeURIComponent(pair[1] || '');
        if (/^tel:\+?\d+/i.test(val)) return val;
        if (/^\+?\d{6,15}$/.test(val)) return 'tel:' + val;
      }

      var m = u.search.match(/(\+?\d{6,15})/);
      if (m) return 'tel:' + m[1];

      return url;
    } catch (e) {
      return url;
    }
  }

  function ymReach(goal) {
    if (typeof ym !== 'undefined') {
      try { ym(COUNTER_ID, 'reachGoal', goal); } catch(e) {}
    }
  }

  function ymReachWithBeacon(goal) {
    if (navigator.sendBeacon && typeof ym !== 'undefined') {
      try { ym(COUNTER_ID, 'reachGoal', goal); } catch(e) {}
    }
  }

  var pending = null, pendingTimeout = null;
  function scheduleAfterSlider(info) {
    pending = info;
    try { sessionStorage.setItem(SS_PENDING_REDIRECT_KEY, JSON.stringify(pending)); } catch(e){}
    if (pendingTimeout) clearTimeout(pendingTimeout);
    pendingTimeout = setTimeout(function() {
      if (pending && pending.type) {
        ymReach('phone_redirect');
        try { sessionStorage.removeItem(SS_PENDING_REDIRECT_KEY); } catch(e){}
      }
      pendingTimeout = null;
    }, DELAY_BEFORE_REDIRECT_GOAL);
  }

  // === UI: слайдер ===
  function showSliderAndCall(rawTarget) {
    var target = unwrapRedirect(rawTarget);

    if (document.getElementById('phone-pre-redirect')) return;

    var overlay = document.createElement('div');
    overlay.id = 'phone-pre-redirect';
    overlay.style.cssText =
      'position:fixed;inset:0;background:rgba(0,0,0,0.6);display:flex;' +
      'align-items:center;justify-content:center;z-index:2147483647';

    var box = document.createElement('div');
    box.style.cssText =
      'background:#fff;border-radius:14px;padding:22px;width:340px;max-width:90%;' +
      'text-align:center;box-shadow:0 8px 30px rgba(0,0,0,.25);font-family:system-ui';

    box.innerHTML =
      '<div style="font-weight:700;font-size:18px;margin-bottom:8px">Позвонить</div>' +
      '<div style="color:#444;font-size:14px;margin-bottom:12px">Проведите ползунок вправо, чтобы подтвердить вызов</div>' +
      '<div id="phone-slider" style="position:relative;height:48px;background:#f0f2f7;border-radius:24px;overflow:hidden;touch-action:none">'+
        '<div id="phone-track" style="position:absolute;inset:0;background:#f0f2f7;"></div>' +
        '<div id="phone-handle" style="position:absolute;left:6px;top:4px;width:40px;height:40px;border-radius:50%;background:#2563eb;color:#fff;display:flex;align-items:center;justify-content:center;cursor:grab;font-size:18px;user-select:none">→</div>' +
        '<div id="phone-text" style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);pointer-events:none;color:#6b7280;font-size:14px">Проведите вправо</div>' +
      '</div>';

    overlay.appendChild(box);
    document.body.appendChild(overlay);

    var container = box.querySelector('#phone-slider');
    var handle = box.querySelector('#phone-handle');
    var track = box.querySelector('#phone-track');

    var maxLeft = 0;
    var isDrag = false, startX = 0, startLeft = 6;

    // ИСПРАВЛЕНИЕ: вычисляем maxLeft после отрисовки через requestAnimationFrame
    requestAnimationFrame(function() {
      maxLeft = container.offsetWidth - handle.offsetWidth - 8;
    });

    function start(e){
      isDrag = true;
      startX = e.touches ? e.touches[0].clientX : e.clientX;
      startLeft = parseInt(handle.style.left) || 6;
      handle.style.cursor = 'grabbing';
      e.preventDefault();
    }
    
    function move(e){
      if (!isDrag) return;
      var x = e.touches ? e.touches[0].clientX : e.clientX;
      var diff = x - startX;
      var newLeft = Math.max(6, Math.min(startLeft + diff, maxLeft));
      handle.style.left = newLeft + 'px';
      var progress = maxLeft > 0 ? (newLeft - 6) / maxLeft : 0;
      track.style.background =
        'linear-gradient(90deg,#a0c3ff ' + (progress*100) + '%, #f0f2f7 ' + (progress*100) + '%)';
      e.preventDefault();
    }
    
    function end(){
      if (!isDrag) return;
      isDrag = false;
      handle.style.cursor = 'grab';

      var pos = parseInt(handle.style.left) || 6;
      if (pos >= maxLeft - 2) {

        ymReach('phone_slider_success');
        scheduleAfterSlider({ type: 'phone', target: target });

        // ИСПРАВЛЕНИЕ: сначала удаляем обработчики, потом делаем переход
        cleanup();

        // === моментальный вызов — без задержек ===
        try {
          if (/^tel:/i.test(target)) location.href = target;
          else location.href = 'tel:' + target.replace(/\D/g,'');
        } catch(e) {
          try { window.open(target, '_blank'); } catch(e){}
        }

      } else {
        handle.style.transition = 'left .22s ease';
        handle.style.left = '6px';
        track.style.background = '#f0f2f7';
        setTimeout(function(){ handle.style.transition = ''; }, 250);
      }
    }

    function onOverlayClick(ev){ 
      if (ev.target === overlay) close(); 
    }

    function onEscape(e){ 
      if(e.key === 'Escape') close(); 
    }

    // ИСПРАВЛЕНИЕ: функция cleanup для удаления всех обработчиков
    function cleanup(){
      document.removeEventListener('mousemove', move);
      document.removeEventListener('mouseup', end);
      document.removeEventListener('touchmove', move);
      document.removeEventListener('touchend', end);
      document.removeEventListener('keydown', onEscape);
      overlay.removeEventListener('click', onOverlayClick);
      handle.removeEventListener('mousedown', start);
      handle.removeEventListener('touchstart', start);
    }

    function close(){
      cleanup();
      try { document.body.removeChild(overlay); } catch(e){}
    }

    // Добавляем все обработчики
    handle.addEventListener('mousedown', start);
    handle.addEventListener('touchstart', start, {passive:false});
    document.addEventListener('mousemove', move);
    document.addEventListener('mouseup', end);
    document.addEventListener('touchmove', move, {passive:false});
    document.addEventListener('touchend', end);
    overlay.addEventListener('click', onOverlayClick);
    document.addEventListener('keydown', onEscape);
  }

  // делегирование кликов
  function extractPhone(target) {
    var a = target.closest && target.closest('a[href]');
    if (a && a.getAttribute) {
      var h = a.getAttribute('href');
      if (h && /^tel:/i.test(h)) return h;
    }
    var n = target.closest && target.closest('[data-phone]');
    if (n) {
      var v = n.getAttribute('data-phone');
      return v ? (/^tel:/i.test(v) ? v : 'tel:' + v.replace(/\D/g,'')) : null;
    }
    return null;
  }

  // ИСПРАВЛЕНИЕ: обработка кликов с дебаунсингом
  ['click','auxclick'].forEach(function(type){
    document.addEventListener(type, function(e){
      // ИСПРАВЛЕНИЕ: добавлен дебаунсинг для защиты от двойных кликов
      var now = Date.now();
      if (now - lastClickTimestamp < 300) return; // игнорируем клики чаще 300ms

      var isMiddle = (type==='auxclick' && e.button===1);
      var isLeftWithMod = (type==='click' && e.button===0 && (e.ctrlKey||e.metaKey||e.shiftKey));
      if (type==='click' && e.button!==0) return;

      var phone = extractPhone(e.target||e.srcElement);
      if (!phone) return;

      lastClickTimestamp = now; // обновляем timestamp только для валидных кликов
      e.preventDefault();

      // ИСПРАВЛЕНИЕ: для Ctrl/Cmd+Click и средней кнопки - сразу открываем без слайдера
      if (isMiddle || isLeftWithMod) {
        try { window.open(phone, '_blank'); } catch(e){}
        return; // не показываем слайдер
      }

      ymReach('phone_click_try');
      showSliderAndCall(phone);
    });
  });

  // ИСПРАВЛЕНИЕ: используем sendBeacon для надежной отправки при закрытии
  window.addEventListener('beforeunload', function(){
    try{
      var s = sessionStorage.getItem(SS_PENDING_REDIRECT_KEY);
      if (!s) return;
      var info = JSON.parse(s);
      if (info && info.type === 'phone') {
        ymReachWithBeacon('phone_redirect');
        sessionStorage.removeItem(SS_PENDING_REDIRECT_KEY);
      }
    }catch(e){}
  });

})();
</script>