Ninja Kill a Rogue JavaScript Event - javascript

Here's the plot, which is a True Story (a problem that exists for a real person, that is - me):
You are working on a large enterprise site, which includes a lot of JavaScript and specifically jQuery code you don't have any control of, and can't possibly change (good luck even finding out who wrote it, or why). Layers of authentication and authority are involved, so just pretend it's written in stone and you can't touch it.
Somewhere in this code, there is an event that scrolls the document to the top of the page after it has loaded. "OK, that sounds harmless" one might think - but it is now your task to scroll the page to a specific item based on a query string or anchor.
Everything works fine generally, but when you click a link that goes to example.com/list#item11, the browser works as expected and you go directly down to the item you want to link to...and then, whammo, the page instantly jumps back to the top of the page.
Now, you might say "well, that's what document.ready() is for!" ...to your horror, you find that the rogue event comes along anyway.
After Stack Overflow searching for an even later event to tie into, you find this gem:
$(document).ready(function(e) {
$(window).load(function(e){ });
}
And surely, this will definitely work! Only, it does not. You try return false and e.preventDefault(), but that does nothing for you here.
All you can be sure of is that this rogue scrolling event occurs after your code runs, after the DOM is ready, and definitely after the window.load() event. You are sure of nothing else.
Can you assassinate this rogue event? Is there some mechanism to intercept scroll events and prevent them from occurring? Can you link into some event later event, like "the DOM is ready, the window is loaded, the page is settled, the children are in bed, and all other events are done being handled.... event()`"?
The only solutions I can imagine now are "give up - scrolling behavior on page load isn't going to work in your scenario", "use a timer and wait! then commit seppuku for being such a dirty hack!", and "ninja-assassination mission!" (since I don't know who wrote the offending code, I'd have to settle for killing their code instead of them - and I'm sure they had their reasons, or have already been assassinated... or at least waiting for the code to pass and do my thing).
Is there some Better Way, some hard to find function, some last resort that invokes the arcane Dark Lords of Reflection, or is it time to give up and solve the problem another way?
TLDR;
How do you stop a disruptive scripted event - like scrolling - from occurring when you can't change the code that is causing it? Acceptable answers include how to make certain your code runs after - without using a timer hack! - and/or if your code always runs first how do you prevent the later code from messing up yours?
It might be helpful to find out how the event is defined, and what events are firing, but I feel that this is a separate question and may not necessarily be required to fix the situation. For illustration purposes, assume there are thousands of active events and listeners spread out across dozens of minified script files. It may just be so hard to narrow down what exactly is happening as to be too much trouble to even try.

The best solution will be to edit the source code where the ready event is declare.
if you can't, you can copy this code somewhere else and edit it.
if its totally not possible, then
you cannot unbind the ready event because that can cause problem.
you can override the window.scrollTop() function by its prototype
window.prototype.scrollTo2 = window.prototype.scrollTo;
window.prototype.scrollTo = function(){
/*Look in the url if you have an hash tab*/
// if yes return false
//if not
window.prototype.scrollTo2()
};

Smack them with a timer if everything else fails:
$(document).ready(function() {
window.setTimeout(function() {
document.location = "#bottom";
}, 200);
});
Live test case.
Ugly, but working.

I would hook into the window.scrollTo prototype to try and catch the burglar in the act. If you know how it's done, it's easier to get rid of it.
If this rogue call is not embedded in too huge a pile of JQuery goo, it could even allow to trace the call to the original culprit, who would soon be smitten with great vengeance and furious anger.

Related

What page events are available to jQuery on which to execute a function?

I've been playing around with javaScript and jQuery to make a chrome extension that will help me at work, however I'm running into issues around the timing of the script.
I've been running it like this:
$(document).ready(function() {
//get stuff, do stuff
})
and banging my head on my desk because it hasn't worked. Then I found the question here about running jQuery from the console, and doing it there, all my selectors returned the right values. Because of this I've concluded there must be some subtlety to the timing.
The page I'm trying to use it on is pretty complex, and $(document).ready() seems to be firing before all of the content is actually loaded.
What are other events that my script could look for to run the code? The actual pages load one after another without any page refreshes, so I think I need to find someway to run my script very frequently and on a new event.
edit: I can't post code because I don't know enough to know what's sensitive and what's not, so I'm erring on the side of not getting fired =P
Also, as for the comment about the page being dynamically rendered, that is absolutely correct -- so I'm trying to figure out some other events to fire on

How do I find what Javascript is running on certain events?

I'll pick Chrome for this example, but I'm open to a solution from any browser.
Use Case:
I have an update button on my website that is used to update item quantities in a shopping cart. I'd like to allow a user to enter a 0 and click update in order to remove the item. Trouble is, there is some listener in some js function that is denying the ability to enter a 0 and click update (after clicking update the old quantity remains).
My question is, what developer tool can I use to find which js function is running during that event? I don't think that Chrome's inspector does this, and I'm not very familiar with Firebug, but I couldn't find the functionality there either.
I feel that I should be able to inspect js firings just like I do css stylings. Is anyone aware of a tool I may use?
I've had to debug some particularly nasty unseen-cause Javascript issues at my job. Knowing the full depth of developer tools like Chrome's is definitely helpful. It undeniably takes some creativity to find places that might be causing the issue, but a few tips:
Tracking down event listeners
Under Chrome's Elements view, try Inspect-ing an element (right-click, Inspect); then, on the right side of the developer view, scroll down to Event Listeners. Here you can view what code files have hooked up an event. Often, this will just point you to a middle-framework from the really devious code you're looking for, but sometimes it will point you in the right direction.
Trapping a DOM modification
Many of the unwanted effects I see are because of something changing some value or attribute on the page that I don't want. Anytime this happens, you can right-click on the element (under the Elements view) and say "Break on..." and the specific scenario you're looking for. When Chrome then hits a breakpoint, you can then look downward in the Stack Trace until you find something recognizable that shouldn't be called.
EDIT after reaching ten votes!
Trapping a JS object modification
If the change you're interested in is code-internal, not in the UI, things get trickier. What's meant by this scenario is that you know somewhere in the code, something incredibly annoying like the following is happening.
company.data.myObject.parameter = undefined;
In this situation, you know myObject is still the same object, but it's being modified, perhaps unintentionally. For that, I often insert the following bit of code, sometimes just through the developer console at some point before said modification happens.
Object.defineProperty(company.data.myObject, 'parameter', {
set: (val) => {
debugger;
}
});
This includes an arrow function - you're only using this for debugging and Chrome supports it, so might as well save keystrokes. What this will do is freeze your debugger as soon as some line of code attempts to modify myObject's "parameter" property. You don't necessarily have to have a global reference to the variable if you can run this line of code from a previous breakpoint that will have the given object in the locals.
Otherwise, if all I'm starting out with is the HTML code, and I want to tie that to Javascript code, I'll often just look for identifying features like "id" elements, and search all JS files in my development directory for it. Normally, I can reach it pretty fast.
Open your page in Firefox with Firebug enabled.
Go to console tab in firebug and click profiling
enter 0 in the textbox and click the button.
Stop profiling.
You will be able to see all the javascript functions which have executed due to your actions. You can view them one by one to figure out which method has caused the mischief.
Go to you code. If you are using jQuery there is going to be a function that will be called with the class or id of that particular update button. Or, if you are using Javascript, there is going to be a function called inside the
<input type="button" name="update" onclick="update()">
These are the two ways to look for the function that is being called; there is no software that I know
Download Firebug for Mozilla Firefox, open it, click on Net and refresh your website. Than, you can see which files are loaded on the page.
If you want to check on errors and what goes wrong with an explanation, than click on console and refresh the page once again. You will see the errors and on which line it goes wrong.
Note: in your console, you can say hold or stop, so that the js file stops loading. And you can edit the script by clicking on script in Firebug. Debugging is simple, as it says on their official page https://getfirebug.com/javascript

Detecting a cancel in navigation with Javascript or jQuery

I was curious if there was a way to detect the user pressing the "stop navigation" button in the browser using javascript (or, even better, jQuery.) For example, if you click a link for a webpage that takes a while to load, you may want to show a spinning loader. But what if the user cancels navigation to the page? Is there anyway to detect that to get rid of the spinning loader that you put?
EDIT: I did a bit more research, and there seems to be an onStop event in javascript but, wouldn't you know it, it only works in internet explorer. If anyone has any other ideas to implement a cross browser solution like onStop, that'd be wonderful, but if not, I'll answer my own question in a few days to close this.
EDIT 2: https://stackoverflow.com/a/16216193 says it's not possible. As do a few other answers.
Alright so, as promised, I'm going to answer my own question.
I've thought about this quite a bit - and I've come up with a solution. I wasn't able to make it work in code (I didn't try too hard), but it should work in theory.
So I thought about the criteria of deciding when a webpage should decide stop was called. I came up with this:
If the script hasn't died after a reasonable amount of time, it can be assumed navigation has been canceled.
Then a jQuery event can be fired on the body or something like that. But what constitutes "a resonable amount of time?" I figured it would be partially based on page render time (fetching images, etc.) to get an idea of how fast the user's internet is. That can be gotten by doing:
var start = new Date();
var time;
$("body").load(function () {
time = new Date() - start;
...
});
Multiply that by a coefficient (maybe 3 or something) and get an approxamate transfer time. (This would have to be adjusted to account for how long it would take for the server to generate the next page, dependent on how dynamic it is.) Then, using this new found time*3 you'd write something like this:
$("a").click(function() { //Anything that could go to another page should filter through here
setInterval(function() {$(document).trigger("navstopped");},time*3);
}
$(document).on("navstopped") {
//Do stuff now that we assume navigation stopped.
}
Assume. That's really all we're doing here. We may have an inconsistent internet connection, fast one minute, slow the next. Server load could be inconsistent too. Maybe it's serving up images like a ninja for this page, but it's hit with a bunch of requests the next, making it generate/serve the next page a bit slower. So we're just assuming that something interrupted the navigation some how, but we are not certain.
Now, of course, this could be used in conjunction with IE's onStop event, but this was really the only cross browser solution I could think of. I wasn't able to get it to work, but maybe some jQuery god may be able to in the future.
Edit before post: Even before I posted this, I had another idea. More browsers support onAbort. If we have a picture that never loads, and the user presses stop, will onAbort be fired? Even if another webpage is loading? It requires testing but that may work too. I like my first idea better though. Although unstable, it is more stable than this cockamamie idea and I realize this could be bad practice.

Non-breaking breakpoints (trace points) in Javascript?

This is a rather complicated question that may simply be impossible with what's currently available, but if there was an easy way of doing it it would be huge.
I'm debugging some JavaScript in Chrome, and because it's very event-driven, I prefer to get trace reports of the code (what got called, etc.) instead of breakpoints. So wherever I leave a breakpoint, I'd like to see the local function name and arguments.
The closest I can get is to drop a conditional breakpoint in, like the following:
There are two big problems with this approach:
Pasting this into each breakpoint is too cumbersome. People would be far more likely to use it if it could be chosen as the default action for each breakpoint.
In Google Chrome, the log calls get fired twice.
Any ideas on a way to surmount either of these problems? I think it might be possible in IE with VS, but the UI there seems equally cumbersome.
IE11 now has "tracepoints", independent of Visual Studio. They do exactly what you asked for three years ago. I don't see them in Chrome or any other browsers yet, but hopefully they will catch on soon!
The best option I found was to edit the javascript code in Chrome's Javascript panel, adding a console.log.
It would only work after the page has been loaded (unless you can afford to put a break point after refresh and then add the logging lines) and (to be worse) you would have to do it each time you reload the page.
Good luck with your search!
I couldn't find something to do this, so I wrote my own.
Now, instead of constantly inserting and removing console.log calls, I leave the logging in and only watch it when necessary.
Warning: specific code below is untested.
var debug = TraceJS.GetLogger("debug", "mousemove");
$('div').mousemove(function(evt) {
debug(this.id, evt);
});
Every time the mouse is moved over a DIV, it generates a logevent tagged ["mousemove", {id of that element}]
The fun part is being able to selectively watch events. When you want to only see mousemove events for element #a, call the following in the console:
TraceJS('a');
When I want to see all mousemove events, you can call:
TraceJS('mousemove');
Only events that match your filter are shown. If you call TraceJS(no argument), the log calls stop being shown.

JQuery domready and window ready randomly working?

I've noticed lately that sometimes the domready and window.load does not work. It's like randomly working when entering the page, and or refreshing.
Say I have:
$(function(){
$('.hide').hide();
// disable html5 native validation to let jquery handle
$('form').attr('novalidate','novalidate');
});
$(window).load(function(){
$('.input').click(function(){
$(this).animate({opacity:0.8});
}).blur(function(){
$(this).animate({opacity:1});
});
});
Sometimes when I load the page, the element is not getting hidden, sometimes it is, the input fields will animate, sometimes not, and both don't necessary fail together. If I refresh the page a few times, it will work.
I always thought that domready will execute as soon as the doms are ready, and window.load will wait until everything on the page is rendered ready? Or is this more bugs from HTML5?
Question is: am I missing something or just misunderstanding something?
Edit: Notably Chromium. I am on Ubuntu, so I would not be surprised if it was a chromium bug.
Be aware that if you have a very complex html structure, it may delay the time for the dom to become ready. The browser probably tries to render the page as quickly as it possibly can, and with a really complex page, it's possible that the rendering will begin and the domready event will trigger, but the browser will quickly render stuff before the specific code that you set up gets triggered.
A block in jQuery domready happens as fast as it can, but if you put, say:
setTimeout(function(){ $().ready(function(){alert('finally');});}, 9000);
That "as fast as it can" is still going to be limited by where the code occurs, in this case after a 9 second timeout.

Categories

Resources