【问题标题】:How to break on the Javascript line of code that triggers a webpage scroll?如何打破触发网页滚动的Javascript代码行?
【发布时间】:2015-11-13 13:56:07
【问题描述】:

我的网页在切换标签时滚动,我正在尝试找出导致此滚动的原因。我可以确认我观察到的行为实际上是滚动,而不是页面高度变化的结果,因为window.pageYOffset 在切换选项卡时会发生变化。

如何找到导致此滚动的代码?

我尝试在 Chrome 中添加滚动事件侦听器断点,但调用堆栈仅列出了此调用: elemData.handle (jquery.js:4334) 指向滚动事件处理代码:eventHandle = elemData.handle = function( e ) {...} 未捕获触发此滚动事件的调用。

然后我阅读了关于使用 JavaScript 的 Object.defineProperty() 方法设置属性更改断点的教程:http://johnkpaul.com/blog/2013/07/20/break-on-property-change/ 并为 window.pageYOffset 编写了一个自定义设置方法,但它没有在页面滚动时触发。这是我使用的代码:

Object.defineProperty(window,'pageYOffset',{
    set:function(val){
        window.pageYOffset=val;
        debugger;
    }
});

我们可以让上述任何一种方法都可以识别滚动触发代码吗?人们是否已经在使用另一种方法来完成这项任务?

【问题讨论】:

  • 如果您点击激活表格的东西是一个锚点,并且您使用href="#tabname" 来标识选项卡,那么可能导致滚动的是浏览器本身:它正试图转到该锚点目标。因此,您将无法在其上设置断点。但如果我是对的,幸运的是,您不必:只需阻止 click 事件的默认操作(即跟随链接),这将停止滚动。
  • defineProperty 调用不起作用也很好。 (我并不惊讶它没有;主机提供的对象不符合 JS 规则。)如果有,你就会得到堆栈溢出,因为代码当然是 in 您的 set 函数正在为属性分配一个值...它调用 set 函数,递归地,永远(嗯,永远,直到你用完堆栈)。
  • 谢谢!您对导致浏览器滚动到该元素的锚标记的 ID 是正确的。我通过反复试验找到了它,但仍然想了解是否有办法通过断点捕获有问题的代码。关于defineProperty() 方法,我应该如何构造setter 方法以避免递归?我按照我链接的博客的模板进行操作。

标签: javascript jquery angularjs debugging browser


【解决方案1】:

我评论了:

如果您单击以激活表格的东西是锚点并且您使用href="#tabname" 来标识选项卡,则可能导致滚动的是浏览器本身:它正试图转到该锚点目标。因此,您将无法在其上设置断点。但如果我是对的,幸运的是,您不必:只需阻止 click 事件的默认操作(即跟随链接),这将停止滚动。

你回复的:

关于导致浏览器滚动到该元素的锚标记的 ID,您是对的。我通过反复试验找到了它,但仍然想了解是否有办法通过断点捕获有问题的代码。

恐怕你不能在浏览器的代码中设置断点。解决这个问题的唯一方法是你和我所做的:反复试验,知道可能导致它的原因,等等。

关于defineProperty() 方法,我应该如何构造setter 方法以避免递归?我遵循了我链接的博客的模板。

在这种情况下(可能还有很多其他情况),您可能肯定不能,因为此属性可能只是窗口对象中某些内部信息的访问器。该特定属性的行为实际上非常有趣(无论如何在 Chrome 上):它由窗口设置除非您将其设置为不同的值。将其设置为不同的值 A) 不滚动窗口,并且 B) 使属性停止告诉您窗口滚动了多远(即使您再次移动它)。

在一般情况下,您只能在以下情况下使用该文章中的技巧:

  • 属性是可配置的

  • 该属性当前没有 getter 或 setter

...在这种情况下,您会这样做(但请参阅下面的更完整的版本):

(function() {
  var value = obj.theProperty;
  Object.defineProperty(obj, "theProperty", {
    enumerable: desc.enumerable,
    get: function() {
      return value;
    },
    set: function(newValue) {
      value = newValue;
      // It changed
    }
  });
})();

请注意我们如何承担存储价值的责任。我们可以将它存储在对象的另一个属性中(例如,___overidden___foo),但是闭包中的变量更简单、更安全。

这是更详尽的版本:

// A function to capture property changes
function callbackOnChange(obj, propName, cb) {
  var value = obj[propName];
  var desc = Object.getOwnPropertyDescriptor(obj, propName);

  // Descriptor will be null if the property doesn't exist yet
  // on the object, either because it doesn't exist *at all*,
  // or because the object is inheriting it. Either way is
  // fine for what we want to do
  if (desc != null) {
    if (!desc.configurable) {
      throw new Error("Property " + propName + " cannot be reconfigured");
    }
    if (desc.set) {
      throw new Error("Property " + propName + " has a setter");
    }
    if (desc.get) {
      throw new Error("Property " + propName + " has a getter");
    }
    if (!desc.writable) {
      throw new Error("Property " + propName + " is not writable, so will never change");
    }
  }
  Object.defineProperty(obj, propName, {
    enumerable: desc.enumerable,
    get: function() {
      return value;
    },
    set: function(newValue) {
      value = newValue;
      cb(obj, propName, newValue);
    }
  });
}

// Create object and property
var obj = {
  foo: "bar"
};

// Set the value, no notification
obj.foo = "update1";

// Capture changes
try {
  callbackOnChange(obj, "foo", function(o, propName, newValue) {
    snippet.log(propName + " changed to " + newValue);
    // Could use debugger; here
  });
} catch (e) {
  snippet.log("Couldn't capture changes: " + e.message);
}

// Set the value, we get a notification
obj.foo = "update2"; // Got notification
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

【讨论】:

  • 非常感谢您提供如此彻底而清晰的答案! :)
猜你喜欢
  • 2021-11-25
  • 2018-06-16
  • 1970-01-01
  • 2010-12-08
  • 2015-12-10
  • 1970-01-01
  • 2014-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多