如何打破触发网页滚动的Javascript代码行?

时间:2015-11-13 13:56:08

标签: javascript jquery angularjs debugging browser

我的网页在切换标签时滚动,我正在试图找出导致此滚动的原因。我可以确认我观察到的行为实际上是一个滚动而不是页面高度变化的结果,因为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编写了一个自定义的setter方法,但它没有在页面上触发滚动。这是我使用的代码:

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

我们可以使上述任何一种方法都能识别滚动触发代码吗?还有其他人已经用于此任务的方式吗?

1 个答案:

答案 0 :(得分:1)

我评论道:

  

如果你点击激活一个表的东西是一个锚,你使用href="#tabname"来识别标签,那么可能导致滚动的是浏览器本身:它正试图去那个锚目标。因此,您将无法在其上设置断点。但是,如果我是对的,幸运的是,你不必:只是阻止click事件的默认操作(这是跟随链接),这将停止滚动。

你回复的

  

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

我不敢在浏览器的代码中设置断点。解决这个问题的唯一方法就是你和我的方式:试验和错误,知道可能导致它的原因等等。

  

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

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

在一般情况下,如果出现以下情况,您只能在该文章中执行此操作:

  • 该属性是可配置的

  • 该属性目前没有getter或setter

...在这种情况下,您可以这样做(但请参阅下面的更多cmoplete版本):

(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>