I have a console on my website which shows a log file, and i've set it up so it refreshes every 500 ms.
jQuery(document).bind("ready", function() {
$("#console").animate({ scrollTop: $(document).height() }, 1000);
function doStuff() {
$( "#console" ).load("console/getlog.php");
$('#console').scrollTop($('#console')[0].scrollHeight);
}
setInterval(doStuff, 500);
});
that works fine, but the problem arrives when i try to select some text in the console. Every time it updates, my selection is cancelled
what can i do to solve this?
Instead of using $( "#console" ).load("console/getlog.php"); to update your console, consider using $( "#console" ).append();. This should prevent the whole thing from reloading and removing your selection.
You can do something like append(getLog()); where getLog() is a function that returns the content of the log. Append can be used with hard coded content (text/html) or the results from a function.
More information on .append() here
Also here - jQuery API doc
Like MelanciaUK suggested, pause the doStuff() when the console is in focus.
You may detect that focus via mouseover/mouseout (so the console stops updating when you hover it), or on mousedown/mouseup, which isn't that usable since you have hardly time to press Cmd + C to save your selection.
Some dirty example: http://jsfiddle.net/ZEW5s/ (updated with pause after mouseup: http://jsfiddle.net/ZEW5s/1/)
tymeJV suggestion to use setTimeout instead of setInterval is a good one, though.
The only way i can think of solving it is by making the delay long so you got time to select the text.
Not much of an answer, but I can't comment, so here it goes.
There are some methods for accessing and manipulating the selection, although they depend on the browser if I remember correctly. Check this out:
https://developer.mozilla.org/en-US/docs/Web/API/Selection
I'd recommend changing the doStuff() to save the current selection, and try to apply it after the loading.
Related
I have been trying to create my custom media player using HTML5 and Jquery.
I have followed different approaches and ran into some trouble based on my way of refreshing the page.
First Case
$(document).ready(function(){
duration = Math.ceil($('audio')[0].duration);
$('#duration').html(duration);
});
In this case, the duration returns NaN when I redirect the page to the same URL by pressing the ENTER key in the address bar. However, it works completely fine when I refresh using the reload button or by pressing the F5 button.
Second Case
I read in some answers that loading duration after the loadedmetadataevent might help. So I tried the following:
$(document).ready(function(){
$('audio').on('loadedmetadata', function(){
duration = Math.ceil($('audio')[0].duration);
$('#duration').html(duration);
});
});
Surprisingly, in this case, the inverse of the first case happened. The duration gets displayed completely fine in the case of a redirect, i.e., pressing ENTER while in the address bar. However, in the case of refreshing using the F5 button or the reload button, the duration doesn't get displayed at all, not even NaN which led me to believe that the code doesn't get executed at all.
Further reading suggested this might be a bug within the webkit browsers but I couldn't find anything conclusive or helpful.
What could be the cause behind this peculiar behavior?
It'd be great if you could explain it along with the solution to this problem.
Edit:
I am mainly looking for an explanation behind this difference in behavior. I would like to understand the mechanism behind rendering a page in the case of redirect and refresh.
It sounds like the problem is that the event handler is set too late, i.e. the audio file has loaded its metadata before the document is even ready.
Try setting the event handler as soon as possible by removing the $(document).ready call:
$('audio').on('loadedmetadata', function(){
duration = Math.ceil($('audio')[0].duration);
$('#duration').html(duration);
});
Note that this requires that the <script> tag be after the <audio> tag in the document.
Alternatively, you can tweak your logic slightly, so that the code that updates the duration always runs (but fails gracefully if it gets a NaN):
function updateDuration() {
var duration = Math.ceil($('audio')[0].duration);
if (duration)
$('#duration').html(duration);
}
$(document).ready(function(){
$('audio').on('loadedmetadata', updateDuration);
updateDuration();
});
Lovely code examples and stuff from people - but the explanation is actually very simple.
If the file is already in the cache then the loadedmetadata event will not fire (nor will a number of other events - basically because they've already fired by the time you attach your listeners) and the duration will be set. If it's not in the cache then the duration will be NaN, and the event will fire.
The solution is sort of simple.
function runWhenLoaded() { /* read duration etc, this = audio element */ }
if (!audio.readyState) { // or $audio[0].readyState
audio.addEventListener("loadedmetadata", runWhenLoaded);
// or $audio.on("loadedmetadata", runWhenLoaded);
} else {
runWhenLoaded.call(audio);
// or runWhenLoaded.call($audio[0]);
}
I've included the jQuery alternatives in the code comments.
According to w3 spec this is standard behavior when duration returns NaN.
So I suggest use durationchange event:
$('audio').on('durationchange', function(){
var duration = $('audio')[0].duration;
if(!isNaN(duration)) {
$('#duration').html(Math.ceil(duration));
}
});
NOTE: This code (and your too) will not work correct in case if you have more than one audio element on page. Reason is that you listen events from all audio elements on page and each element will fire own event:
$('audio').on('durationchange', function(){...});
OR
You can try:
<script>
function durationchange() {
var duration = $('audio')[0].duration;
if(!isNaN(duration)) {
$('#duration').html(Math.ceil(duration));
}
}
</script>
<audio ondurationchange="durationchange()">
<source src="test.mp3" type="audio/mpeg">
</audio>
Note that behaviors will differ from one browser to another. On Chrome, you have different type of loading. When resources are not in cache, it will fetch either the complete file (for js or css for example), either a part of the file (mp3 for example). This partial file contains metadata that allows browser to determine duration and other data such as the time it'll take to download whole file at this rate, trigerring for example canplay or canplaythrough events. If you look at network usage in you dev console, you'll see that the HTTP status code will be either 200 (succesful load) or 206(partial load - for mp3 for example).
When you hit refresh, elements are checked to see if they changed. HTTP status will then be 304, meaning file hasn't been modified. If it hasn't changed and is still in browser cache, then it won't be downloaded. The call to determine if it has or not changed comes from the server providing the file.
When ou simply click enter in adress bar, it's automatically taken from cache, not validating online. So it's much faster.
So depending on how you call or refresh your page (either simmple enter, refresh or complete refresh without cache), you'll have big differences on the moment you get the metadata from your mp3. Between taking the metadata from cache directly vs making a request to a server, the difference can be a few hundreds milliseconds, which is enough to change what data is available at different moment.
That being said, listening to loadedmetada should give consistent result. This event is triggered when the data with duration information is loaded, so whatever way the page is loaded, it shouldn't matter if that called is properly made. At this point you have to consider maybe some interference from other elements. What you should do is follow your audio through various events to get exactly where its at at different moments. So in you document ready you could add listeners for different event and see where the problem occurs. Like this:
$('audio')[0].addEventListener('loadstart', check_event)
$('audio')[0].addEventListener('loadeddata', check_event)
$('audio')[0].addEventListener('loadedmetadata', check_event)//at this point you should be able to call duration
$('audio')[0].addEventListener('canplay', check_event) //and so on
function check_event(e) {
console.log(e.target, e.type)
}
You'll see that depending on the way you refresh, these events can come at different moments, maybe explaining inconsistencies in your outputs.
Today one of the weirdest things i have ever seens just happened.
I have a loop in a page that does a synchronous ajax request for each element of the loop. Since it was taking too long i decided to stop the loop by refreshing the page.
When the page loaded i couldn't click on any element of the page so i checked Firebug console and i saw that the ajax calls of the previous loop were still being done (the loop is set to start after i click on a link, so it can't start as soon as page loads).
To stop the loop i had to close the current tab and open my page in a new one.
It's worth mentioning that i have a datatables table on that page and i have enabled the option to save the table state, maybe this is interfering with my code (the loop itself is not part of the datatables initialization, though it uses data from the table).
I have also noticed that some things of the loop aren't being done. It should've changed the page of the datatable by time to time, and it should've written in the javascript console, both things don't happen after i refresh the page, the ajax calls are still going though.
Here's the code where the loop is contained:
//This variable will contain a reference to the datatable
var oTable;
var isWorking=false;
//Change the datatables page
function changeTablePage(oTable, page, clear){
if(clear){
oTable.fnClearTable();
oTable.fnDraw();
}
oTable.fnPageChange(page);
}
//This part is inside a document.ready block
$(document).on("click", ".validation-all-click", function(event){
if(!isWorking){
isWorking=true;
//saves the current page, will be needed for later
var page=oTable.fnPagingInfo().iPage;
var numberPage=0;
var done=false;
while(!done){
console.log("page: "+numberPage);
changeTablePage(oTable, numberPage, false);
var nNodes = oTable.fnGetNodes();
var len=nNodes.length;
$('.validation-click', nNodes).each(function( index, value ){
//id of the element to be validated
var id=$(value).data("rowid");
//Calls the validation url
$.ajax({
url: "/validate?id="+id,
async: false
});
});
//if the datatables page has 0 items, then it's done
if(len==0){
done=true;
}
numberPage++;
}
//goes back to the original page
changeTablePage(oTable, page, true);
isWorking=false;
}
});
I don't want this behavior to happen, but i have no idea on why it's happening and how to prevent it
Thanks for sharing your story. I don't see any specific question in the post, so I'll just comment on whatever I feel like.
As I understand it, browsers aren't required to redraw the UI while JavaScript is running. Since there's a script blocking on a synchronous ajax request, maybe it's to be expected that you don't see the changes to the table until the loop finishes.
Furthermore, it may also be that the browser isn't required to destroy a page while its scripts are running. That would explain why you saw requests in Firebug after refreshing the page--perhaps the previous copy of the page was still running in a hidden state.
edit: valepu reports in a comment, below, that the table does change while the script is running. That's fine. The browser can probably determine that it can redraw the UI during the ajax call (which doesn't affect the JavaScript environment). valepu also clarifies that the visual updates stop after refreshing the page, though the requests continue to go out. This is also consistent with the idea that the browser has just hidden the previous page (until it finishes) and loaded up a new copy of the page when refreshing.
As for how to prevent it: the most reliable way would be to use asynchronous requests, or otherwise yield between requests. Sorry, folks.
I have solved by adding a variable that will stop the loop when i exit the page:
var stopLoop=false
$( window ).unload(function() {
stopLoop=true;
});
both loops will now check if this variable is false before executing the code inside the loop.
This works on Firefox but not on Chrome though.
-- EDIT --
In the end i have solved by editing the code in order to make the ajax calls asynchronous and using the callback functions to continue the cycle, though it was no simple task (some days later i found a new solution that allowed me to do all that i needed in a single call when i found out how to recover the parameters used by datatables to retrieve data, but this has nothing to do with the original question). So, for future references: expect this "weird" behaviour when making a loop with ajax synchronous calls
I am trying to restore scroll event or scrollbar position after page refresh/reload. Here is my code, but I can't make it work. Tried it on JSFiddle but no luck. Please help! JavaScript code below.
Note: I have a hidden <input> element with an id of scrolled, and a <textarea> with an id of textAreaInput.
function saveScroll()
{
some code
}
function restoreScroll()
{
some code
}
window.onunload = saveScroll;
window.onload = restoreScroll;
So, you don't actually set the #scrolled value when producing the page because its value set in saveScroll never reaches the server. Remember that when a page is reloaded, all the DOM data from the previous load are discarded.
I'd figure that you could try setting this value in a document.cookie but you need to check if this is going to work. Another option is to use window.sessionStorage if one is available in the target environment.
Here is a fiddle for you to play with. Scroll the textarea and refresh the page. You can see the debug output in your firebug or devtools console.
I'm working on a extension for a fb game that adds some extra info to popup boxes, hides some useless information and run some timers so you can see how long till you have to do something even if you are not on the game page.
My problem is that a while back they went over to using ajax to change between pages and I am wondering if I am using the right approach to handle this.
I run a setInterval that checks if I am on a page where i want to add/remove soemting and i havent done it yet, if this is true I do my stuff, the code looks something like this:
function myFunction() {
if($(selector for some element I am looking for).length > 0 && $(selector for some item I add).length == 0) {
//do some stuff
}
}
setInterval("myFunction()",1000);
Is this the right way of handling ajax page change from an content script in an extension?
If so is there a better way to see if I am on the right page or if i have added to this page already other than doing a selection?
You can use livequery jquery plugin to catch when new element is created on a page:
$("#ajax-element").livequery(function({
//ajax-element is created
});
You can also listen to DOMSubtreeModified event which fires when DOM changes:
document.addEventListener("DOMSubtreeModified", function(event){
//something has changed, possibly ajax-element was added
});
There are also more specific DOMNodeInsertedIntoDocument and DOMNodeRemovedFromDocument events.
I have gone insane trying to figure out how to make this work. Code looks roughly like:
function onDropDownChanged() {
$("#updatePanel").load(
"myUrl",
{ id: $("#myDropDown option:selected").val() },
onPanelLoaded
);
}
function onPanelLoaded() {
$("#theTextInput").focus();
}
$(document).ready(function() {
$("#myDropDown").change(onDropDownChanged);
}
The first time the change handler is fired, it does the ajax update, and the text box is focused.
However, on subsequent changes, it continues to do the ajax update, but the text box is never focused again.
I found that if in onDropDownChanged, I added $("#updatePanel").empty() before the ajax call, the text box would always get focused. The problem with that is the entire form disappears for a second, causing an ugly flash. Given ajax is supposed to make things like this nice, it's not a workaround I want to use.
It seems like it should work, but I wonder if the DOM isn't updated by the time the callback is invoked. You might want to see if introducing a delay helps.
function onPanelLoaded() {
setTimeout( function() { $("#theTextInput").focus(); }, 500 );
}
Including the HTML on the page and what is returned via load() would be helpful if this doesn't work.
I had a similar problem with IE6 and IE7, but setTimeout() was not a reliable solution. Sometimes it worked, sometimes it did not. It didn't work on some machines at all, and the 500ms value was completely arbitrary anyway. The focus() function worked exactly as expected without any timeout in both Firefox and Chrome, of course.
My solution was to call focus() twice for IE:
function onPanelLoaded() {
var panel = $('#theTextInput');
panel.focus();
panel.focus();
}
Now THAT did what I intended in the first place.