Reputation: 2703
My webpage scrolls upon switching tabs and I am trying to figure out what is causing this scroll. I can confirm that the behavior I observe is actually a scroll and not a result of change in page height as window.pageYOffset
changes upon switching tabs.
How can I find the code that is causing this scroll?
I tried adding a scroll event listener breakpoint in Chrome but the call stack listed just this call:
elemData.handle (jquery.js:4334)
which points to the scroll event handling code: eventHandle = elemData.handle = function( e ) {...}
The call that triggered this scroll event was not captured.
Then I read this tutorial on using Object.defineProperty()
method of Javascript to set breakpoints on property change: http://johnkpaul.com/blog/2013/07/20/break-on-property-change/ and wrote a custom setter method for window.pageYOffset
but it is not getting triggered on page scroll. Here is the code that I used:
Object.defineProperty(window,'pageYOffset',{
set:function(val){
window.pageYOffset=val;
debugger;
}
});
Can we make either of the above methods work to identify the scroll triggering code? Is there another way that people already use for this task?
Upvotes: 1
Views: 235
Reputation: 1075209
I commented:
If the thing you click to activate a table is an anchor and you use
href="#tabname"
to identify the tab, what's probably causing the scroll is the browser itself: It's trying to go to that anchor target. So you won't be able to set a breakpoint on it. But if I'm right, fortunately, you don't have to: Just prevent theclick
event's default action (which is to follow the link), and that will stop the scrolling.
to which you replied:
You are right about the anchor tag's ID causing the browser to scroll to that element. I found it by trial and error but would still like to understand if there's a way to catch the offending code via breakpoints.
You can't set breakpoints in the browser's code I'm afraid. The only way to figure this out was the way you and I did: Trial and error, knowing what might cause it, etc.
Regarding the defineProperty() method, how should I have structured the setter method to avoid recursion? I followed the template of the blog I linked.
You probably certainly can't, in this case (and probably many, many others), because this property is likely just an accessor for some internal information in the window object. The behavior of that particular property is actually really interesting (on Chrome, anyway): It gets set by the window unless you set it to a different value. Setting it to a different value A) Doesn't scroll the window, and B) Makes the property stop telling you how far the window has scrolled (even if you move it again).
In the general case, you can only do the trick in that article if:
The property is configurable
The property does not currently have either a getter or a setter
...in which case you'd do it something like this (but see below for a more cmoplete version):
(function() {
var value = obj.theProperty;
Object.defineProperty(obj, "theProperty", {
enumerable: desc.enumerable,
get: function() {
return value;
},
set: function(newValue) {
value = newValue;
// It changed
}
});
})();
Notice how we take over the responsibility of storing the value. We could store it on another property on the object (say, ___overidden___foo
), but a variable in the closure is easier and safer.
Here's the more thorough version:
// 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>
Upvotes: 1