I'm having a problem where sometimes when my JavaScript in a Web page gets the value of window.pageYOffset it is inexplicably 0 even though I know the user is viewing the middle of the document and its value should be huge, like 650000. Note that a huge percentage of the time I get a reasonable value. But sometimes it's zero and sometimes it's a seemingly random small value, like in the 6000 range when I'm expecting 650000.
Rather than post a bunch of code, I'd like to ask some general questions to help me figure out where to begin to look.
This page is being displayed in an iOS WKWebView (though this problem can manifest in a similar context in an Android app). JavaScript methods in my app can be invoked in one of several ways:
When my app is notified that the page has finished loading (via a delegate method), it invokes a JavaScript method using evaluateJavaScript from the Objective-C code.
My app can call evaluateJavaScript at other times, not just when the page finishes loading.
A JavaScript function may be called as the result of a timer firing.
A JavaScript function may be called as the result of a scroll event.
I have been operating under the assumption that the JavaScript code on the page is always operating in a single thread. That is, I don't have a situation where a timer firing, a scroll event happening, or even a call from the Objective-C code (using evaluateJavaScript) is interrupting anything that might be happening in the JavaScript runtime. So I shouldn't have to worry about interrupting some system-level activity that is modifying window.pageYOffset while I'm trying to access it.
So that's my first question: Am I correct that someone outside my code is invoking my JavaScript methods on a single thread and not monkeying with the DOM on another thread?
My second question is related: My code modifies the DOM, adding and removing div elements. I've been assuming that those modifications are synchronous -- if I insert an element with insertAfter or insertBefore, I expect that the child/parent/sibling pointers are accurate upon return, and I assume that I can immediately access things like the top and left values on some other element and they will have been updated to reflect the inserted/removed element. The point being that I shouldn't have to "wait" for the DOM to "stabilize" after making changes and before checking something like window.pageYOffset. Is this correct?
One more clue: To help mitigate this, I have had good luck simply testing window.pageYOffset for zero at the top of a function. If it is zero, I call myself back on a timer (with just a 1 msec delay). If I do that long enough, it will eventually be non-zero.
Perhaps after reading all this, none of the detail is relevant and you know the answer to the basic question: Why do I sometimes get an invalid value (usually 0) in window.pageYOffset when the same line of code gives a valid value at other times.
The problem turned out to be that there appears to be a period of time between when I give the WKWebView a new HTML string to render and when it tells me that it is done loading the page that the existing page is still active. During this time, timers continue to fire, but some document and window properties will not be valid.
Because of the difficulty of debugging JavaScript running in this environment, I was tricking myself into thinking "eventually pageYOffset becomes valid" when in fact what I was seeing was that the new page eventually finished loading, and it was this new page that was generating valid calls to my timer functions.
In my particular case (may not work for everyone) I am able to detect the value of window.pageYOffset at the top of my timer function and if it is 0, call myself back after a brief delay. This allows me to handle the case where, for some reason, window.pageYOffset is just not yet valid (my test will eventually pass and my timer function will continue as usual) and the case where everything is in the process of being thrown away in favor of the new page (in which case the timer will not fire because the page goes away).
Related
I'm debugging a userscript, which is hard to debug, because the bug happens rarely and unpredictably (yet), and the function stack gets executed repeatedly (and the bug happens approximately once every 10-20 times). I cannot pause the devtools, otherwise the page will reload and the bug won't appear (so I cannot use breakpoints). Edit: even if I stop the page from reloading I still have to make user-input repeatedly and fast to cause the bug, so not possible to use breakpoints, because that would stop the bug from appearing.
One of the ways which I can think of is putting console.log in every function and logging variables and then searching through them to find where it went wrong. But it seems very tedious. In the performance section of devtools it's possible to record the process, and see the function calls, but it's impossible to see the variables.
Is there any way to go through the function call stack post-factum (maybe a tool other than devtools), seeing what the variables were equal to in every cycle? (And not only the current call stack, because it gets updated on every user input in my script). Or is console.logging my only option in this case?
I thought that "step out of the current function" option would work, but it seems to be unpredictable (it seemed to happen at least when there is setTimeout). And the function stack gets updated on every user input (and to capture this bug the user input sequence should be fast), so there is no way to go through the call stack either.
It would be perfect to have something like a call stack tree, which would show when a certain function was last called (and how many times during a certain period of time or since the page load), what was the position of this function in the tree of all functions, and the ability to go through this tree and search for all instances of calls to a specific function, and then the ability to see what the variables were equal to in every call.
There is a lot of code, but since it was requested, here is a link to the code inside a GitHub repository (it's a Chrome Extension, and the code where the bug happens is inside the objGA object [at the 6490's line of code]). And the bug happens somewhere between the 6600's line and the 6890's (the bug is that sometimes it doesn't move chess pieces on premoves). It's a Chrome Extension to move chess pieces with a keyboard and a mouse.
https://github.com/Sentero-esp12/Lichess-Keyboard-WebRequest-/blob/master/Extension/code.js
Edit:
Luckily I was able to reproduce it in a slow pace with breakpoints (was able to find the exact moment when the bug appeared). So it turned out I was using Math.round instead of Math.floor trying to extract the first digit from a double digit number. Since only some numbers were >= *5, the bug appeared only occasionally and I couldn't notice the cause.
I'm trying to identify roughly when the DOM is finished updating after a page is loaded via AJAX on any arbitrary website.
My current method first listens for the chrome.webNavigation.onHistoryStateUpdated event in a background script, then executes a content script in which a MutationObserver detects changes to the website's body. From there, unfortunately, it seems like it's a bit more finicky. If I just wait for the first mutation where nodes are added to the DOM, I wind up in many cases (YouTube, to give one example) where the page is still blank. Other more hacky approaches I've considered include things like just using setTimeout or waiting for the page to reach a certain length, but those seem clearly wide open to exception cases.
Is there a more fool-proof way to detect that the DOM has roughly finished updating? It doesn't necessarily have to be perfectly precise, and erring on the side of triggering late in my use case is better than triggering early. Also it isn't important at all that resources like video and images be fully loaded, just that the text contents of the page are basically in place.
Thanks for your help!
First of all, apologies if this question was answered before.
I'm writing a code in JS to read an Excel File, get the value of the first cell in the column, search for it (it's an ISBN code, which I'm searching with the Google Books API) and get other relevant info, made available through the search (like Title, Subtitle and Author), then proceed to the next line and repeat the process.
My problem is writing the new data back in the Excel File. The code is writing all info in the last used row in the file. While using window.alert to flag the code, I noticed that when the alert was in a for loop, right before the search was initiated, the new data was inserted just fine, but if I tried to use a pause (like a timer function or a while loop to consume time) it didn't help at all.
What I want to know is why that behavior might be happening and, if possible, of course, a possible solution for my problem, since having to use alert as a pause isn't exactly the most interesting solution.
Thanks in advance
Alert will always stop all execution of code, except for web workers. Therefore, If you need to continue execution, use a web worker. Have a look at this for reference (the note part covers this topic partially)
When browsers show a native modal interaction widget, such as an alert, it transitions into a state that waits for the response. In this state, it is allowed to redraw the page and process certain low level events. Here's the code from Mozilla Firefox that alert() and confirm() use:
http://mxr.mozilla.org/mozilla-central/source/toolkit/components/prompts/src/nsPrompter.js#434
This openRemotePrompt function doesn't return until the user clicks "OK" on the alert. However browser behaves differently while the alert is open. A loop repeatedly calls thread.processNextEvent to do certain kinds of work until the dialog is closed. (It doesn't run the application's JavaScript code, since that's meant to be single-threaded.)
When you use a pure JavaScript busy wait, for example, by looping until a certain wall time, the browser doesn't take these measures to keep things moving. Most noticeably, the UI won't redraw while the JavaScript code is looping.
Is JavaScript intended to be running as little as possible on a website/webapp? By that I mean is the usual intention to run through all your js files as soon as the page loads and put them aside, and then when functions come up to execute them right away and be done with it?
I'm working on a project using google maps and I have a custom marker object scripted out, and a debugger has told me that the browser runs through all my js files before anything even appears on the page.
My problem comes in here: I wanted to animate certain markers to bounce up and down continuously with jQuery (similar to OS X icons in the dock) and my several attempts at infinite loop functions all just crash the browser. So I understand that the browser doesn't like that, but is there a way to have a simple script be repeating itself in the background while the user navigates the page? Or is JavaScript just not supposed to be used that way?
(I worked with Flash for a long time so my mindset is still there.)
Yes, Javascript functions should just do their bit and exit as soon as possible. The GUI and the scripts run on the same single thread, so as long as you are inside a Javascript function, nothing shows up in the browser. If you try to use an infinite loop, the browser will appear to freeze.
You use the window.setInterval and window.setTimeout methods to trigger code that runs at a specific time. By running an interval that updates something several times a second, you can create an animation.
You have to set a timer to execute a script after a defined time.
var timer = setTimeout(code, milliseconds);
will execute code in so-and-so milliseconds. Each execution of the script can set a new timer to execute the script again.
You can cancel a timed event using clearTimeout(timer).
Use setTimeout() or setInterval(). The MDC articles on it are pretty good.
You'll need to update inside of functions that run quickly, but get called many times, instead of updating inside of a loop.
Since you said that you are using jQuery, consider using its effects API (e.g., jQuery.animate()), it will make your life much easier!
Personally, I save as much code as possible for execution after the page has loaded, partly by putting all my <script>s at the bottom of <body>. This means a (perceived) reduction in page load time, whilst having all my JS ready to run when need be.
I wouldn't recommend going through everything you need to do at the beginning of the document. Instead, bind things to events such as clicks of buttons, etc.
Suppose I modify the HTML DOM on line 1. Can I be sure that line 2 of the JavaScript will be working with the DOM modifications enacted by line 1?
This is the only explanation I can come up with some buggy behavior I've been having on a form. The previous line is supposed to update the DOM, but sometimes the DOM is not updates by the time it's not on the next one. Things seem to work fine when I go slower though.
Yes, the Javascript DOM modifications will occur sequentially, unless you are waiting for an asynchronous AJAX call to return. The next instruction will not occur until the first has completed. However please show your code!
Updating a specific property on a DOM element happens right away and should persist on a subsequent read of that property.
If you are relying on that change to propagate across the the DOM, it can be tricky. For example, such as changing the size of an element and expecting the sibling element to report a new offset position as a result - the latter may may not happen until the stack unwinds. I don't actually know the exact rules, but you have to be careful - and it is sometimes browser dependent behavior. And scary yet, sometimes throwing an alert to help debug this makes the elements "realize" their new layout right away. Then you take the alert out and it goes back to buggy behavior.
So if you are positive that a DOM change hasn't had the impact right away, then sometimes the thing to do is to call "setTimeout" with a callback function and a time value of 0. When the timer callback completes, you can complete the subsequent processing. YMMV