RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 988695
Accepted
Родион Поляков
Родион Поляков
Asked:2020-06-03 17:37:00 +0000 UTC2020-06-03 17:37:00 +0000 UTC 2020-06-03 17:37:00 +0000 UTC

查看一页网站的深度

  • 772

我正在尝试编写一个脚本来测量单页网站的浏览深度。

这个想法如下:一个单页的网站被分成几个部分。查看任何部分超过 15 秒时,将触发 Yandex.Metrica 目标。

部分示例。

<section id=""></section>
<section id=""></section>
<section id=""></section>

特定部分的定义。

// Получаем нужный элемент
var element = document.querySelector('target');

var Visible = function (target) {

    // Все позиции элемента
    var targetPosition = {
            top: window.pageYOffset + target.getBoundingClientRect().top,
            bottom: window.pageYOffset + target.getBoundingClientRect().bottom
        },
        // Получаем позиции окна
        windowPosition = {
            top: window.pageYOffset,
            bottom: window.pageYOffset + document.documentElement.clientHeight
        };

    if (targetPosition.bottom > windowPosition.top && // Если позиция нижней части элемента больше позиции верхней чайти окна, то элемент виден сверху
        targetPosition.top < windowPosition.bottom) { // Если позиция верхней части элемента меньше позиции нижней чайти окна, то элемент виден снизу
        // Если элемент полностью видно, то запускаем следующий код
        console.clear();
        console.log('Вы видите элемент!');
    } else {
        // Если элемент не видно, то запускаем этот код
        console.clear();
    };

};

// Запускаем функцию при прокрутке страницы
window.addEventListener('scroll', function() {
    Visible (element);
});

我无法解决两个任务,否则脚本不能被认为是有效的:

1.) 多个部分。我不明白如何实现一种方便的方法来枚举所需的部分以及如果用户在某个部分停止时激活计时器的机制。

2.) 最大的问题是一个用户可以同时看到 2 或 3 个部分。示例:用户在 100% 时看到第 1 部分,在 20% 时看到第 2 部分。或者,用户在 20% 时看到第 2 部分,在 100% 时看到第 3 部分,在 20% 时看到第 4 部分。我不明白如何为用户视野中最多的部分激活计时器,并且当滚动到另一个部分时,重新启动计时器。示例:在用户的视野中,第 1 部分为 100%,第 2 部分为 20%。对于第 1 部分,计时器计数为 14 秒,但随后用户滚动页面,结果发现第 2 部分在 20% 时可见,第 3 部分在 100% 时可见,第 4 部分在 20% 时可见,这意味着您需要更新计时器并开始第 3 节的新报告。

在此处输入图像描述

这两个问题是相互关联的,如果我可以借助某些拐杖自己解决第一个问题,那么我几天都无法将它们连接在一起。解决这个问题的任何想法?

javascript
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Stranger in the Q
    2020-06-03T19:39:51Z2020-06-03T19:39:51Z

    但是,如果我们计算用户总共看到了哪个元素的表面的百分比呢?

    例如,在 1 秒内查看该元素 100% 的元素区域,计数器增加 1,如果只有 20% 的元素可见,则为 0.2

    let handle = s => {
      let b = s.getBoundingClientRect();
      s.square = b.bottom > 0 ? (Math.min(b.bottom, window.innerHeight)-Math.max(0, b.top))/b.height : 0;
      s.style.background = `linear-gradient(to right, #d77 ${s.square*100}%, wheat ${s.square*100}%)`
    }
    
    let sections = [...document.querySelectorAll('section')]
    
    window.onscroll = e => sections.forEach(handle)
    window.onscroll()
    
    setInterval(i => {
    
      sections.forEach(s => s.textContent = +s.textContent + Math.max(0, s.square)*0.1)
      document.querySelector('span').textContent = sections.map(s => (+s.textContent).toFixed(1))
    
    },100)
    section {
      width: calc(100vw - 35px);
      margin-bottom: 8px
    }
    
    span {
      position:fixed;
      top:50%;
      left:50%;
      transform:translate(-50%,-50%);
      border:solid;
      font-size:30px;
      padding:3px;
    }
    <span></span>
    <section style="height:100px"> </section>
    <section style="height:150px"> </section>
    <section style="height:300px"> </section>
    <section style="height:200px"> </section>
    <section style="height:100px"> </section>
    <section style="height:200px"> </section>
    <section style="height:300px"> </section>
    <section style="height:400px"> </section>
    <section style="height:100px"> </section>
    <section style="height:130px"> </section>
    <section style="height:150px"> </section>
    <section style="height:400px"> </section>

    该块的绘制取决于它可见的百分比,这是为了清晰和调试..

    您还可以添加一个阈值来确定,例如 20% ...

    PS:有一堆超时的解决方案可以调试总比没有好,我建议避免这种方法到最后..

    • 4
  2. Skywave
    2020-06-04T13:01:07Z2020-06-04T13:01:07Z

    它可以通过设置一个变量viewTimeouts来存储每个元素的超时时间来通过超时来实现。好吧,我们设置了边界条件。

    var elements = document.querySelectorAll('section'),
        viewTimeouts = {},
        secondsLimit = 4, // сколько секунд ждем, чтобы среагировать на просмотр
        viewPercentLimit = 80; // Минимальный процент видимости элемента
    

    我还做了一个方法VisibleElements,将遍历所有元素并比较位置。在启动时运行它也是值得的,以便在页面加载后立即进行分析,而不仅仅是在滚动之后。

    var VisibleElements = function(elements){
        for(var i=0; i<elements.length; i++) {
            var element = elements[i];
            // надо как-то идентифицировать элементы
            if(!element.dataset['id']) {
                element.dataset['id'] = genElementId();
            }
            var elementId = element.dataset['id'],
                isVisible = Visible(element);
            if (isVisible) {
                if (!viewTimeouts[elementId]) {
                    // если человек видит элемент и таймаут еще не запущен - запускаем таймаут
                    viewTimeouts[elementId] = setTimeout(deepViewed(element), secondsLimit * 1000);
                }
            } else {
                // если элемент не виден - чистим таймаут
                clearTimeout(viewTimeouts[elementId]);
                viewTimeouts[elementId] = null;
            }
        }
    };
    

    更正了该方法Visible,以便在以最小百分比查看元素时返回 true。

    var Visible = function (target) {
        // Все позиции элемента
        var targetPosition = {
            top: window.pageYOffset + target.getBoundingClientRect().top,
            bottom: window.pageYOffset + target.getBoundingClientRect().bottom
        },
        // Получаем позиции окна
        windowPosition = {
            top: window.pageYOffset,
            bottom: window.pageYOffset + document.documentElement.clientHeight
        };
    
        if (targetPosition.bottom > windowPosition.top && // Если позиция нижней части элемента больше позиции верхней чайти окна, то элемент виден сверху
            targetPosition.top < windowPosition.bottom) { // Если позиция верхней части элемента меньше позиции нижней чайти окна, то элемент виден снизу
            // Если элемент полностью видно, то запускаем следующий код
            var targetSize = targetPosition.bottom - targetPosition.top,
                targetVisibleTop = targetPosition.top > windowPosition.top ? targetPosition.top : windowPosition.top,
                targetVisibleBottom = targetPosition.bottom < windowPosition.bottom ? targetPosition.bottom : windowPosition.bottom,
                viewSize = targetVisibleBottom - targetVisibleTop,
                percent = 100 * viewSize / targetSize;
            return percent > viewPercentLimit;
        } else {
            // Если элемент не видно, то запускаем этот код
        };
    
    };
    

    https://jsfiddle.net/skywave/6vacfyL0/75/

    • 2
  3. Best Answer
    user256824
    2020-06-17T23:33:52Z2020-06-17T23:33:52Z

    使用IntersectionObserver的变体。实施方案如下:

    • 通过选择器选择元素列表css;
    • 创建一个部分的虚拟表,我们在其中写入id可见高度;
    • 当改变截面与可见区域的交点时,我们重写了表格中元素的可见高度;
    • 通过表格中高度的最大值,我们确定前导部分;
    • 如果新的前导部分与前一个不同,则启动计时器;
    • 在计时器倒计时结束时,id演示中会显示来自前导部分的消息。

    // Количество секунд, когда будет определен лидер.
    const REMAINING_TIME = 8;
    
    // Параметры для наблюдателя.
    const OPTIONS = {
      // Корневой элемент для пересечения видимости.
      // По умолчанию `viewport` документа.
      root: null,
      // Порог срабатывания наблюдателя за пересечением видимости.
      threshold: [0, .2, .4, .6, .8, 1]
    }
    
    // Выбираем секции, за которыми будем устанавливать наблюдения.
    const SECTIONS = document.querySelectorAll('.intersection');
    
    // Секции, как участники детской игры "Царь горы".
    let conquerors = [];
    // Текущий победитель.
    let winner;
    // Идентификатор таймера.
    let timerId;
    // Счетчик для таймера.
    let remaining;
    
    function decrement() {
      // Очищаем предыдущий таймер, чтобы не было зацикливаний.
      clearTimeout(timerId);
    
      if (remaining > 0) {
        timerId = setTimeout(decrement, 1000);
      } else {
        alert(`Прошло ${REMAINING_TIME} сек просмотра. Победитель: + ${winner.id}`);
      }
    
      // Распечатка результатов для демки.
      printResult();
    
      // Минусуем счетчик для таймера.
      remaining--;
    }
    
    // Обновление данных о секции в таблице секций.
    function update(id, ratio, height) {
      // Текущая секция из таблицы секций.
      let conqueror = conquerors.find(i => id === i.id);
    
      conqueror.ratio = ratio.toFixed(4);
      conqueror.height = height.toFixed(4);
    
      // Обновим данные о победителе.
      // Победитель определяется по
      // максимально видимой высоте элемента.
      winner = conquerors.reduce(function(prev, curr) {
        return parseFloat(curr.height) > parseFloat(prev.height) ? curr : prev;
      });
    }
    
    // Функция обратного вызова для наблюдателя. Будет вызываться, когда
    // каждый элемент из набора `entries`
    // появляется и исчезает из области видимости.
    function computedTimeOut(entries, observer) {
      entries.forEach(function(entry, index) {
        // Элемент, видимость которого изменилась.
        let element = entry.target;
    
        // Коэффициент пересечения текущего элемента с `viewport`.
        let ratio = entry.intersectionRatio;
    
        // Видимая высота элемента.
        let height = entry.intersectionRect.height;
    
        // Сохраним `id` текущего победителя до того, как обновим таблицу.
        let winner_id = winner ? winner.id : winner;
    
        // Обновим данные о секции в таблице секций.
        update(element.id, ratio, height);
    
        // Если секция попала в поле видимости.
        if (entry.isIntersecting) {
          // Визуализируем стиль секции.
          visualize(element, ratio);
    
          // Если победитель еще не был определен либо он сменился.
          if (!winner_id || winner.id !== winner_id) {
            // Сбрасываем счетчик на значение,
            // заданное в качестве шаблонного.
            remaining = REMAINING_TIME;
    
            // Запускаем таймер.
            decrement();
          }
        }
      });
    }
    
    // Создаем экземпляр наблюдателя за пересечением видимости элементов.
    let visibleDetails = new IntersectionObserver(computedTimeOut, OPTIONS);
    
    // Перебираем все секции.
    SECTIONS.forEach(function(section, index) {
      // Устанавливаем наблюдателя за секцией.
      visibleDetails.observe(section);
    
      // Заполняем таблицу секций.
      // Используем атрибут `id` для идентификации элемента.
      conquerors.push({
        id: section.id,
        ratio: 0,
        height: 0
      });
    
      // Для демки добавим разную высоту секциям: < 1440px.
      section.style.height = Math.random() * 1440 + 'px';
    })
    
    /**
     * Дальше идут константы и функции, которые используются для демки.
     */
    
    // Спаны для вывода коэффициентов пересечения.
    const RATIO_SPANS = document.querySelectorAll('.intersection__ratio');
    
    // Спан для вывода времени пересечения.
    const TIMING_SPAN = document.querySelector('#intersection__timing');
    
    // Спан для вывода таблицы секций.
    const CONQUERORS_SPAN = document.querySelector('#intersection__conquerors');
    
    // Спан для вывода победителя.
    const WINNER_SPAN = document.querySelector('#intersection__winner');
    
    // Визуализация наблюдений.
    // Вызывается при изменении пересечений
    // областей видимости секции с `vieport`.
    function visualize(element, ratio) {
      let span = element.querySelector(".intersection__ratio");
      span.textContent = element.id + ' ' + (Math.floor(ratio * 100)) + "%";
      element.style.color = ratio > 0.5 ? '#fff' : '#bbb';
      element.style.backgroundColor = `rgba(195, 195, 195, ${ratio})`;
    }
    
    // Распечатка результатов для демки.
    // Вызывается при изменении счетчика и таймера.
    // Все переменные глобальные.
    function printResult() {
      TIMING_SPAN.textContent = remaining > 0 ? remaining : 'Таймер остановлен.';
      CONQUERORS_SPAN.textContent = JSON.stringify(conquerors, null, 2);
      WINNER_SPAN.textContent = JSON.stringify(winner, null, 2);
    }
    body {
      margin: 0;
    }
    
    #app {
      max-width: 280px;
      margin: 0 auto;
    }
    
    #intersection__timing {
      position: fixed;
      top: 8px;
      left: 16px;
    }
    
    #intersection__winner {
      position: fixed;
      bottom: 8px;
      left: 16px;
      color: red;
      font-size: 18px;
    }
    
    #intersection__conquerors {
      position: fixed;
      top: 8px;
      right: 16px;
    }
    
    .intersection {
      height: 220px;
      border: 1px solid #ccc;
      text-align: center;
      font-family: cursive;
    }
    
    .intersection__ratio {
      position: sticky;
      top: 0;
    }
    <div id="app">
      <section id="first" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
      <section id="second" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
      <section id="third" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
      <section id="fourth" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
      <section id="fifth" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
      <section id="sixth" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
      <section id="seventh" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
      <section id="eighth" class="intersection"><span class="intersection__ratio"></span><span class="intersection__timing"></span></section>
    
      <div id="intersection__timing"></div>
      <pre id="intersection__winner"></pre>
      <pre id="intersection__conquerors"></pre>
    </div>

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5