JavaScript TinyMCE/jQuery race condition on firefox - javascript

I have a website with a form that uses TinyMCE; independently, I use jQuery. When I load the form from staging server on Firefox 3 (MacOS X, Linux), TinyMCE doesn't finish loading. There is an error in Firefox console, saying that t.getBody() returned null. t.getBody(), as far as I understand from TinyMCE docs, is a function that returns document's body element to be inspected for some features. Problem doesn't occur when I use Safari, nor when I use Firefox with the same site running from localhost.
Original, failing JavaScript-related code looked like this:
<script type="text/javascript" src="http://static.alfa.foo.pl/json2.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.ui.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({ mode:"specific_textareas", editor_selector:"mce", theme:"simple", language:"pl" });
</script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.jeditable.js"></script>
<script type="text/javascript" src="http://static.alfa.foo.pl/jquery.tinymce.js"></script>
<script type="text/javascript" charset="utf-8" src="http://static.alfa.foo.pl/foo.js"></script>
<script type="text/javascript">
$(document).ready(function(){
/* jQuery initialization */ });
</script>
I tried changing script loading order, moving tinyMCE.init() call to the <script/> tag containing $(document).ready() call—before, after, and inside this call. No result. When tinyMCE.init() was called from within $(document).ready() handler, the browser did hang on request—looks like it was too late to call the init function.
Then, after googling a bit about using TinyMCE together with jQuery, I changed tinyMCE.init() call to:
tinyMCE.init({ mode:"none", theme:"simple", language:"pl" });
and added following jQuery call to the $(document).ready() handler:
$(".mce").each( function(i) { tinyMCE.execCommand("mceAddControl",true,this.id); });
Still the same error. But, and here's where things start to look like real voodoo, when I added alert(i); before the tinyMCE.execCommand() call, alerts were given, and TinyMCE textareas were initialized correctly. I figured this can be a matter of delay introduced by waiting for user dismissing the alert, so I introduced a second of delay by changing the call, still within the $(document).ready() handler, to following:
setTimeout('$(".mce").each( function(i) { tinyMCE.execCommand("mceAddControl",true,this.id); });',1000);
With the timeout, TinyMCE textareas initialize correctly, but it's duct taping around the real problem. The problem looks like an evident race condition (especially when I consider that on the same browser, but when server is on localhost, problem doesn't occur). But isn't JavaScript execution single-threaded? Could anybody please enlighten me as to what's going on here, where is the actual problem, and what can I do to have it actually fixed?

The browser executes scripts in the order they're loaded, not written. Your immediate scripts -- tinyMCE.init(...) and $(document.ready(...)); -- can execute before the files finish loading.
So, the problem is probably network latency -- especially with 6 separate scripts (each requiring a different HTTP conversation between the browser and server). So, the browser is probably trying to execute tinyMCE.init() before tiny_mce.js has finished being parsed and tinyMCE is fully defined.
If don't have Firebug, get it. ;)
It has a Net tab that will show you how long it's taking all of your scripts to load.
While you may consider the setTimeout to be duct taping, it's actually a decent solution. Only problem I see is that it assumes 1 second will always fix. A fast connection and they could see the pause. A slow connection and it doesn't wait long enough -- you still get the error.
Alternatively, you might be able to use window.onload -- assuming jQuery isn't already using it. (Can anyone else verify?)
window.onload = function () {
tinyMCE.init(...);
$(document).ready(...);
};
Also, was that a direct copy?
<script type="text/javascript">
$(document).ready(function(){
/* jQuery initialization */ }
</script>
It's missing the ) ending ready:
<script type="text/javascript">
$(document).ready(function(){
/* jQuery initialization */ })
</script>
Missing punctuation can cause plenty of damage. The parser is just going to keep reading until it finds it -- messing up anything in between.

Since this is the first page which came in google when I asked myself the same question, this is what i found about this problem.
source
There's a callback function in tinyMCE which is fired when the component is loaded and ready. you can use it like this :
tinyMCE.init({
...
setup : function(ed) {
ed.onInit.add(function(ed) {
console.log('Editor is loaded: ' + ed.id);
});
}
});

If you are using jquery.tinymce.js then you don't need tiny_mce.js because TinyMCE will try to load it with an ajax request. If you are finding that window.tinymce (or simply tinymce) is undefined then this means that the ajax is not yet complete (which might explain why using setTimeout worked for you). This is the typical order of events:
Load jquery.js with a script tag (or google load).
Load TinyMCE's jQuery plugin, jquery.tinymce.js, with a script tag.
Document ready event fires; this is where you call .tinymce(settings) on your textareas. E.g.
$('textarea').tinymce({ script_url: '/tiny_mce/tiny_mce.js' })
Load tiny_mce.js this step is done for you by TinyMCE's jQuery plugin, but it could happen after the document ready event fires.
Sometimes you might really need to access window.tinymce, here's the safest way to do it:
$(document).tinymce({
'script_url': '/tiny_mce/tiny_mce.js'
'setup': function() {
alert(tinymce);
}
});
TinyMCE will go so far as to create a tinymce.Editor object and execute the setup callback. None of the editor's events are triggered and the editor object created for the document is not added to tinymce.editors.
I also found that TinyMCE's ajax call was interfering with my .ajaxStop functions so I also used a setTimeout:
$(document).tinymce({
'script_url': '/tiny_mce/tiny_mce.js'
'setup': function() {
setTimeout(function () {
$(document).ajaxStart(function(e) {/* stuff /});
$(document).ajaxStop(function(e) {/ stuff */});
}, 0);
}
});

Related

Javascript works only on reloading page

On https://bm-translations.de/dolmetscher-franzoesisch-deutsch.php you can see the Header doesnt work like it does here: https://bm-translations.de
I guess out of some reason JS isnt working. But the strange thing is on reloading the page (strg + F5) its working. Whats wrong here?
I am loading Jquery first and in footer my own JS.
This is most likely a race condition. You're probably running your javascript before the DOM has had a chance to load. The reason it works on the second reload is that the browser is using the cached version of the scripts.
There are a few ways you can deal with this:
Make sure you script tags don't have an async attribute. This tells them they're allowed to load in any order they'd like so your script may load before jQuery has had a chance to load.
Make sure your scripts are in the right order. The simplest is to move your scripts into the bottom of the body so that the browser loads it last. Make sure your scripts go after any dependent scripts, such as jQuery.
<script src="jquery.js"></script>
<script src="myscript.js"></script>
</body>
The other option is to use a "ready" event. Since you're using JQuery, you already have access to this by simply wrapping your function in a jQuery object with a callback function.
console.log('this will run before the DOM is loaded');
// wait for DOM to load inside jquery callback
$(function() {
console.log('this will run after the DOM has loaded');
});
console.log('this will also run before the DOM is loaded');
The output of the above code would read:
'this will run before the DOM is loaded'
'this will also run before the DOM is loaded'
'this will also run before the DOM is loaded'
because you have added async attribute to jquery script tag.
you can not use the async attribute here if some other script is depending on jquery.
in https://bm-translations.de/dolmetscher-franzoesisch-deutsch.php
<script async src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
</script>
<script async src="https://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js"></script>
while in https://bm-translations.de
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
</script>
<script async="" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js"></script>
more about async attribute.

jQuery: confirm if .html() is addable

To save up on performance, I load social links by adding them when user hovers over the follow/like buttons. Example:
jQuery(document).ready(function(){
jQuery('.trigger').bind('click mouseover', function(){
jQuery(this).html('<script src="http://platform.twitter.com/widgets.js"></script> Follow #twitter ');
jQuery('.trigger').unbind('click mouseover');
});
});
Works fine. In fact, you should do this as well.
The problem is that in Firefox when you use incognito mode, it blocks social links, so .html() doesn't output anything.
The same problem with some browser add-ons that blocks social links.
Is there anyway to find out if .html() did output the code, and if not, just add a link like:
jQuery(this).html('Follow');
You need to load the script yourself rather than this way and then check if it succeeded:
jQuery(document).ready(function($) {
$('.trigger').one('click mouseover', function(){
// we add just the HTML manually
$(this).html(' Follow #twitter ');
// and the script with jQuery
$.getScript("http://platform.twitter.com/widgets.js").fail(function(err) {
// react to errors here, for example set the .html
}).done(function(success) {
// here we can be sure the script loaded.
});
});
});
Note that a particularly clever ad blocker could intercept the request and return an empty response instead of a 404 - but in practice none do.
I also generally disagree with your assertion that they should be lazy loaded - in general it's mostly fine to just add the word async to the script and it won't block the page loading and load later:
<!--- NOTE THE 'async' --->
<script async src="http://platform.twitter.com/widgets.js"></script>
Follow #twitter
async - Set this Boolean attribute to indicate that the browser should, if possible, execute the script asynchronously. It has no effect on inline scripts (i.e., scripts that don't have the src attribute).
From MDN.
As general code review, ready takes a parameter which you can use to short name jQuery (as $ commonly), you should use the standard.on in favor of the deprecated deprecated .bind (and .off for unbind).
Update, I already realized that what you were doing with the bind/unbind (on/off) pair is essentially a one which fires an event only once on the first time - so I changed it to that.

Why does jQuery's ajax automatically run scripts?

I noticed recently that if jQuery ajax is called right after injecting jQuery into an inner iframe, jQuery loses its functions - like jQuery(..).dialog(), .draggable, and any other plugins. If the ajax call is commented out, the jQuery works fine. Is this a known bug, or something I'm doing wrong? This problem can be seen in this file, with jQuery in the same directory:
<html>
<head>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<script src="jquery.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
</head>
<body>
Try and <button id="btn">load</button>
<iframe width=300 height=300></iframe>
<script>
"use strict";
jQuery('#btn').click(function(){
var $ = jQuery;
console.log(typeof jQuery('iframe').dialog);
var doc = jQuery('iframe')[0].contentDocument;
function insertscript(src) {
var newscript = doc.createElement('script');
newscript.setAttribute('src',src);
doc.documentElement.appendChild(newscript);
}
insertscript('jquery.js');
//This breaks the jQuery plugins:
var test = $.get('jquery.js',function(){
//Now we know jQuery should be in the frame.
});
//So does this:
//jQuery.ajax({url:'http://192.168.1.17/wordpress/wp-includes/js/jquery/jquery.js',cache:true,processData:false});
console.log(typeof jQuery('iframe').dialog);
window.setTimeout(function(){
//jQuery is no longer the original jQuery object. Note the cached reference $().dialog does exist though.
console.log('after awhile... dialog is ' + typeof jQuery('iframe').dialog);
},3000)
//jQuery.ajax({url:jqurl,cache:true,processData:false});
});
</script>
</body></html>
This is a minimal sample of the problem, making sure the iframe has loaded a certain jQuery.js (then ajax should have the cached script) before some other stuff is added to the iframe.
Click load, and after while, console log will show "after awhile... dialog is undefined" - only when ajax was used.
Update: It looks like $.get('jquery.js') actually runs the script. $.get('alert.js') shows an alert, when alert.js has an alert function. (In the case of jQuery, re-defining the global jQuery reference.) Why does jQuery's ajax have this behavior? Does this happen with all ajax implementations?
As someone answered earlier (whose answer got deleted?), jQuery ajax automatically chooses what to do depending on what type of content you requested. (An unfortunately under-documented feature). loading an external js will not just return when the browser has fetched the script, it will also run the script.
Whenever you re-include jQuery at a later point, it rewrites the window.jQuery object, therefore removing the jQuery.prototype.dialog, etc.
The Firefox .watch function can be helpful in cases like this, to see where something got redefined. This, for example, would give you a stack trace of anything that redefines jQuery:
window.watch('jQuery',function() { console.trace() } )

How to execute code before window.load and after DOM has been loaded?

Here is the circumstance:
I have 2 pages:
1 x html page
1 x external Javascript
Now in the html page, there will be internal Javascript coding to allow the placement of the window.onload, and other page specific methods/functions.
But, in the external Javascript I want certain things to be done before the window.onload event is triggered. This is to allow customized components to be initialized first.
Is there a way to ensure initialization to occur in the external Javascript before the window.onload event is triggered?
The reason I have asked this, is to attempt to make reusable code (build once - use all over), to which the external script must check that it is in 'order/check' before the Javascript in the main html/jsp/asp/PHP page takes over. And also I am not looking for a solution in jQuery #_#
Here are some of the links on Stack Overflow I have browsed through for a solution:
Javascript - How to detect if document has loaded (IE 7/Firefox 3)
How to check if page has FULLY loaded(scripts and all)?
Execute Javascript When Page Has Fully Loaded
Can someone help or direct me to a solution, your help will be muchness of greatness appreciated.
[updated response - 19 November 2012]
Hi all, thanks for you advice and suggested solutions, they have all been useful in the search and testing for a viable solution.
Though I feel that I am not 100% satisfied with my own results, I know your advice and help has moved me closer to a solution, and may indeed aid others in a similar situation.
Here is what I have come up with:
test_page.html
<html>
<head>
<title></title>
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript" src="test_script_1.js"></script>
<script type="text/javascript" src="test_script_2.js"></script>
<script type="text/javascript">
window.onload = function() {
document.getElementById("div_1").innerHTML = "window.onload complete!";
}
</script>
<style type="text/css">
div {
border:thin solid #000000;
width:500px;
}
</head>
<body>
<div id="div_1"></div>
<br/><br/>
<div id="div_2"></div>
<br/><br/>
<div id="div_3"></div>
</body>
</html>
loader.js
var Loader = {
methods_arr : [],
init_Loader : new function() {
document.onreadystatechange = function(e) {
if (document.readyState == "complete") {
for (var i = 0; i < Loader.methods_arr.length; i++) {
Loader.method_arr[i]();
}
}
}
},
load : function(method) {
Loader.methods_arr.push(method);
}
}
test_script_1.js
Loader.load(function(){initTestScript1();});
function initTestScript1() {
document.getElementById("div_1").innerHTML = "Test Script 1 Initialized!";
}
test_script_2.js
Loader.load(function(){initTestScript2();});
function initTestScript2() {
document.getElementById("div_2").innerHTML = "Test Script 2 Initialized!";
}
This will ensure that scripts are invoked before invocation of the window.onload event handler, but also ensuring that the document is rendered first.
What do you think of this possible solution?
Thanking you all again for the aid and help :D
Basically, you're looking for this:
document.onreadystatechange = function(e)
{
if (document.readyState === 'complete')
{
//dom is ready, window.onload fires later
}
};
window.onload = function(e)
{
//document.readyState will be complete, it's one of the requirements for the window.onload event to be fired
//do stuff for when everything is loaded
};
see MDN for more details.
Do keep in mind that the DOM might be loaded here, but that doesn't mean that the external js file has been loaded, so you might not have access to all the functions/objects that are defined in that script. If you want to check for that, you'll have to use window.onload, to ensure that all external resources have been loaded, too.
So, basically, in your external script, you'll be needing 2 event handlers: one for the readystatechange, which does what you need to be done on DOMready, and a window.onload, which will, by definition, be fired after the document is ready. (this checks if the page is fully loaded).
Just so you know, in IE<9 window.onload causes a memory leak (because the DOM and the JScript engine are two separate entities, the window object never gets unloaded fully, and the listener isn't GC'ed). There is a way to fix this, which I've posted here, it's quite verbose, though, but just so you know...
If you want something to be done right away without waiting for any event then you can just do it in the JavaScript - you don't have to do anything for your code to run right away, just don't do anything that would make your code wait. So it's actually easier than waiting for events.
For example if you have this HTML:
<div id=one></div>
<script src="your-script.js"></script>
<div id=two></div>
then whatever code is in your-script.js will be run after the div with id=one but before the div with id=two is parsed. Just don't register event callbacks but do what you need right away in your JavaScript.
javascript runs from top to bottom. this means.. if you include your external javascript before your internal javascript it would simply run before the internal javascript runs.
It is also possible to use the DOMContentLoaded event of the Window interface.
addEventListener("DOMContentLoaded", function() {
// Your code goes here
});
The above code is actually adding the event listener to the window object, though it's not qualified as window.addEventListener because the window object is also the global scope of JavaScript code in webpages.
DOMContentLoaded happens before load, when images and other parts of the webpage aren't still fully loaded. However, all the elements added to the DOM within the initial call stack are guaranteed to be already added to their parents prior to this event.
You can find the official documentation here.

$("<img/>").attr("src",something).load() not supported in IE < 9?

http://jsfiddle.net/DerekL/qDqZF/
$('<img/>').attr('src', "http://derek1906.site50.net/navbar/images/pic3.png").load(function() {
$("body").html("done");
blah blah blah...
})
There I have tested using $("<img/>").load in IE 7, and what I got are these:
When run in counsel, I get this:
"Unable to get value of the property 'attr': object is null or undefined"
When used in my webpage, I get this:
"SCRIPT5007: Unable to get value of the property 'slice': object is null or undefined"
jquery.js, line 2 character 32039
What happened? (Hate IE...)
Ensure that the load function is being executed. I recently dealt with this issue. In IE the load function wasn't firing on cached images. For my purposes I was able to get around this by never allowing the image to cache. ( An ugly solution )
ex:
src="loremIpsum.jpg"
change to:
src="loremIpsum.jpg?nocache=" + new Date().getTime()
Where "nocache" can be changed to anything that makes sense to you.
From the jQuery documentaion:
Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load() shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache"
http://api.jquery.com/load-event/
In IE the load event doesn't always get triggered when the image is cached. Try this:
var img = new Image();
img.src = "http://derek1906.site50.net//navbar/images/pic3.png";
if (img.complete || img.readyState === 4) {
$("body").html("done");
}
else {
$(img).on("load error onreadystatechange",function(e){
if (e.type === "error") {
$("body").html("Image failed to load.");
}
else {
$("body").html("done");
}
});
}
Also, don't forget to wait for the DOMReady event, otherwise $("body") may not exist yet if the image loads fast enough.
jsFiddle
Edit:
I have a plugin that may help simplify image preloading: https://github.com/tentonaxe/jQuery-preloadImages/
So I did some quick testing in jfidde, and pulled out the relevant code and ran it standalone in ie7-8-9. They all ran fine. I can say with confidence that it is not this piece of code that is breaking your page.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title> - jsFiddle demo by DerekL</title>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.7.1.js'></script>
<script type='text/javascript'>
$('<img/>').attr('src', "http://derek1906.site50.net//navbar/images/pic3.png").load(function() {
$("body").html("done");
$("<img/>").appendTo("body").attr("src","http://derek1906.site50.net//navbar/images/pic3.png");
});
</script>
</head>
<body>
Loading...
</body>
</html>
Some ideas though:
Wrap any script in the document head that manipulates the DOM in a document.ready call.
$(document).ready(function(){ go(); });
Search your source code for that slice call. If you are trying to manipulate a jQuery collection with .slice() it will break. jQuery collections are Objects, not Arrays. (may or may not be your problem)
Make sure that any code trying to touch that image is called after the .load() method returns. A common mistake is to do something like:
$('<img id="me" />').attr('src', 'my.jpeg')
.load( function(){ $(this).appendTo("body"); } );
alert( $('#me').attr('src') );
If this is the only image on the page, the above script will likely fail because the appendTo() is called asyncronously, and almost certianly after the following lines of code have executed and failed. Make sure that any logic manipulating that image is run from the .load() callback. (As you have nicely done in your above example code.)
If you paste the rest of your page source I can take a look! Good luck!
add load event first then set img'src
because ie run so fast than when you set the src, "load" event was finished
the new load handler will be executed next change

Categories