Cufon loaded asynchronously doesn't render in IE - javascript

I'm creating a site which uses Cufon and is particularly heavy in terms of page-weight due to a large amount of Javascript. Therefore I'm trying to load in the script asynchronously with head.js ( http://headjs.com/ ) like so:
head.js("http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js", function() {
head.js("/js/libs/cufon-yui.js", function() {
head.js("/js/shared/Stag_Bold_700.font.js" , function() {
Cufon.replace('h1', { fontFamily: 'Stag Bold' });
});
});
});
So Jquery is downloaded first, the subsequent cufon lib file and cufon font are downloaded in sequence and then Cufon is finally called to replace the H1. Obviously, this is a trimmed down example with fewer replacements but this still doesn't work when just attempting to replace the H1.
The problem is that ONLY in Internet Explorer (6/7/8), the text is not replaced but I can see that Cufon has definitely been called. I can ascertain this because the tag has the class "cufon-active cufon-ready" added to it. When I inspect the markup using the IE Developer toolbar, the cufon/cufoncanvas tags are there inside the selected elements but are, for want of a better word, invisible.
In IE9, the script behaves as intended similar to Chrome and Firefox. I have tried adjusting the Cufon drawing engine and have updated to the latest 1.09i version for good measure. If I move the Cufon calling statements to the document ready event instead of loading asynchronously, it works but I am trying to optimize page load and my site will be using a number of Cufon fonts as well as many other JS plug-ins. I've also tried using both labs.js and head.js to load the appropriate files asynchronously.

I had the same problem - I addressed this by using the browser detection of head.js to do the following:
if (head.browser.mozilla || head.browser.webkit || head.browser.opera ||
(head.browser.ie && (head.browser.version == '9.0'))) {
head.js('script/jquery.min.js',
'script/cufon.js', function () {
head.js('script/bebas_neue_400.font.js', function () {
Cufon.replace('h1', {
textShadow: '1px 1px rgba(0, 0, 0, 0.2)'
}).now();
// or a head.js('scripts/file.with.cufon.replacement.js');
});
});
} else {
// here we load scripts depending on GZIP support for this browser
document.write('\x3Cscript type="text/javascript" src="script/jquery.min.js">\x3C/script>');
document.write('\x3Cscript type="text/javascript" src="script/cufon.js">\x3C/script>');
document.write('\x3Cscript type="text/javascript" src="script/bebas_neue_400.font.js">\x3C/script>');
document.write('\x3Cscript type="text/javascript" src="script/file.with.cufon.replacement.js">\x3C/script>');
}
You could also use conditional comments (I didn't because I am also doing GZIP support detection in JavaScript and needed to adjust the scripts which are loaded dynamically.)
It's a hack, but should be useful enough until it's addressed within the library itself.
(I have also posted GIST with a more complete example here)

try calling
<script type="text/javascript"> Cufon.now(); </script>
just before </body> tag closes.

Try adding Cufon.now() after the Cufon.replace call, like so:
Cufon.replace('h1', { fontFamily: 'Stag Bold' });
Cufon.now();

I solved this in a way similar to CameraSchoolDropout's approach, except instead of using Document.write, I'm using IE conditional tags, and YepNope.js.
This issue on github says that they had problems using document.createElement('script'), and I just felt better using IE conditionals.
You can see an example page that I created at http://epraxadev.com/yepnope/
<head>
<style type="text/css">
#txt { visibility:hidden; }
</style>
<!--[if lte IE 8]>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script src="js/cufon-yui.js"></script>
<script src="js/adventor.cufon.js"></script>
<![endif]-->
<script src="js/modernizr.custom.js"></script>
<script>
yepnope([{
test: window.jQuery,
nope: {
'yJ': '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js',
'yCufon': 'js/cufon-yui.js',
'yFont': 'js/museo.font.js'
},
callback: {
'yJ': function(){
console.log("YepNope loaded jQuery! This isn't IE!");
}
},
complete: function() {
console.log('All browsers, including IE, will show this');
Cufon.replace('h1');
$(document).ready(function(){
$('#txt').css('visibility', 'visible');
});
}
}]);
</script>
<noscript>
<style type="text/css">
#txt { visibility:visible; }
</style>
</noscript>
</head>

For now just load jQuery and Cufón using regular <script> tags and load the subsequent files using a script loader.
Using document.write is a bad approach since it will only work if the script is loaded/executed before DOMReady and using browser sniffing to do it isn't a good approach either since it can give false results.
Conditional comments aren't a good solution either because you may need to update the scripts in the future and you will have to remember to update it in 2 different places which is bad for maintainability.
Follow this issue on GitHub to know when the bug is fixed.

Related

Correct JavaScript equivalent for <body onload=>

What is the correct JavaScript way to replacement <body onload="init();">, bearing in mind that we no-longer have need to support very old browsers.
In my case I want to add a onClick event to all tags and would like to keep the Javascript separate to my HTML page.
window.onload = init();
Started off with this but found the global document object is not available inside init(), this seems to be it seems to be a timing issue. Did it work better in older browsers?
document.addEventListener("DOMContentLoaded", init, false);
Seems to be a more modern reliable way but is this supported by all modern browsers?
Then there is the suggestion to just put the init() at the bottom of the page but that is obviously getting back to having the Javascript direct in the HTML.
<script type="text/javascript">init();</script>
Is there a definitive way I should be running my init code?
I think what you want is $(function(){}), or $(document).ready(...), as Marc B mentioned. That seems to accomplish what you're asking for, unless I'm misunderstanding your question. The jQuery API backs this up.
The .ready() method offers a way to run JavaScript code as soon as the page's Document Object Model (DOM) becomes safe to manipulate.
Most simple method is adding a script tag at the end of body with a self invoking anonymous function:
<body>
<!-- content here -->
<script type="text/javascript">
(function() {
//Run init code here
})();
</script>
</body>
For external js files:
<body>
<!-- content here -->
<script src="main.js" type="text/javascript"></script>
</body>
main.js file:
(function() {
//Run init code here
})();
Now that the problem of the extra brackets has been solved the final solution which I am just about to test is:
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init);
} else {
window.onload = init;
}
I know JQuery can do this with it's .ready event but some of these pages are very small pages of Ajax content and I would prefer to avoid the overhead of JQuery if it is not necessary.

Javascript conflict in Wordpress between Theme and Gallery Javascript

I have been working on a specific justified gallery for more time than I care to admit, but I have finally managed to get the gallery looking how I need and all the javascript working.
I did all my testing in Dreamweaver but when it finally came to moving what I had into my Wordpress website there seems to be a conflict with some of the javascript already on the site and the javascript I need to make my gallery work.
I have included all my custom javascript into the footer as follows:
JAVASCRIPT
<script src="http://dangoodeofficial.co.uk/wp-content/plugins/Justified-Gallery/libs/jquery/jquery.min.js"></script>
<script src="http://dangoodeofficial.co.uk/wp-content/plugins/Justified-Gallery/dist/js/jquery.justifiedGallery.min.js"></script>
<link rel="stylesheet" href="http://dangoodeofficial.co.uk/wp-content/plugins/Justified-Gallery/dist/css/justifiedGallery.min.css" type="text/css" media="all">
<script>
jQuery(document).ready(function($) {
$('.x-nav > li.current-menu-item').removeClass("current-menu-item");
});
</script>
<script type="text/javascript">
jQuery( document ).ready(function() {
jQuery('.flip-btn-1').click(function(e) {
e.preventDefault();
jQuery(".front").toggleClass('flip');
jQuery(".back").toggleClass('flip');
});
});
jQuery( document ).ready(function() {
jQuery('.flip-btn-2').click(function(e) {
e.preventDefault();
jQuery(".front2").toggleClass('flip2');
jQuery(".back2").toggleClass('flip2');
});
});
</script>
<script>
$('#liveDemo').justifiedGallery({
rowHeight : 190,
sizeRangeSuffixes: {
'lt100':'_t',
'lt240':'_m',
'lt320':'_n',
'lt500':'',
'lt640':'_z',
'lt1024':'_b'
}
}).on('jg.complete', function () {
$(this).find('a').colorbox(colorboxConf);
});
</script>
I have figured out What is causing the conflict is the jquery.min.js It is stoping Revolution Slider from working and also causing a problem with a testimonial slider (it shows all the slides at once), and my fixed nav-bar is no longer fixed.
Is there a way to find out what is causing the conflict?
Website in question is www.dangoodeofficial.co.uk
Thank you,
Dan
You shouldn't include Javascript libraries like jquery.justifiedGallery.min.js and jQuery in the footer by a link; you need to correctly enqueue Javascript in WordPress in the theme's functions.php file. See https://codex.wordpress.org/Function_Reference/wp_enqueue_script
As a result, you have two copies of the main jQuery library being loaded.
The jQuery document ready functions can be added in the header or footer with <script type="text/javascript">**</script> tags.
You've got lots of Javascript errors in the console. Use the developer tools in Firefox (or Firebug) or Chrome or Safari or IE to see what javascript is loading on your site and the errors.
The cause of the conflict could be the inclusion of the jquery.js file. You don't need to include jquery separately in your plugin. By default, WordPress is shipped with a jquery file and it is available for use off the shelf.

How can I change my script order when the HTML cannot be modified?

Current:
<head>
<script src="Custom.JS">
<script src="jQuery.JS">
</head>
Need:
<head>
<script src="jQuery.JS">
<script src="Custom.JS">
</head>
I can't edit the document, but I can edit the script files. Any way to do this?
This is a bad scenario and the root cause should be fixed, however.. there are some "approaches".
Here is one that I am presenting. It assumes that jQuery.js cannot (and should not) be modified and should still contain all the relevant jQuery code, but Custom.js is fair-game and the code it runs can be treated asynchronously.
Consider if Custom.js looks like so (modify/fix as required):
(function (fn) {
if (window.jQuery) {
// jQuery already loaded, don't reload/reevaluate it
fn(window.jQuery);
} else {
// Use of document.write because document.head is not supported before IE9.
// (If IE9+ was the minimum version I would recommend doing the script
// insertions via DOM and using onload, as these would be available then.)
window.__CustomJs_OnLoad = function () {
// This now runs after jQuery is loaded (from the injected script)
del window.__CustomJs_OnLoad;
fn(window.jQuery);
};
// Script.onload could be used IE8+, but why not go all-the-way awesome?
// These scripts are guaranteed to run synchronously in document-order.
document.write("<script src=jQuery.js></sc" + "ript>");
document.write("<script>__CustomJs_OnLoad()</sc" + "ript>");
}
}(function ($) {
// The rest of Custom.js here - which should already be nicely wrapped
// in a function/module anyway, no?
}));
You can have enclose the entire body of custom.js in a function and then edit jQuery.js so that when it's done with its work it calls that function in custom.js, effectively switching their execution order.
Or, If you don't have control over jQuery.js, you can just copy and paste it's contents into the to of custom.js. That easy you'll have access to the jQuery functions, and when the jQuery.js file loads, it'll still be ok (it'll just redefine everything)
Try
<head>
<script src="jQuery.JS">
</head>
js
$.holdReady(true);
$.getScript("Custom.JS", function(script) {
$.holdReady(false);
})
$(document).ready(function) {
// do stuff
});

What prevents Require'ed scripts from injecting more script tags

I am trying to use some older scripts in an AMD/Require site. Some of these older scripts inject other scripts through the use of document.write or document.writeln of tags. Below is a simplified form of what's happening:
script1.js:
console.log('script1');
document.writeln("<script src='script2.js'></script>");
script2.js
console.log('script2');
If I load script1 in the classic way, DOM shows both script tags are present and console output shows that both are executed:
<head>
<script src="script1.js"></script>
</head>
<body></body>
BUT If I load script1 via RequireJS, DOM shows only the script1 tag. Console output shows that script1 is executed, but the document.writeln is apparently ignored and thus script2 is not added to the DOM:
<head>
<script src="require-jquery.js"></script>
<script language="javascript">
require( ['script1'], function( ) { } );
</script>
</head>
<body></body>
What prevents this additional script from being inserted into the DOM? I'm suspect I would have other issues with load order even if the above did work correctly. But I want to understand this hole in my knowledge of what RequireJS is doing differently that prevents the additional script from loading.
I am using version 2.1.4 of RequireJS. The behavior is the same on Firefox and Chromium.
EDIT: I forgot to mention that in the real scenario, the path to 'script2' is dynamic based on some server-side logic as the first script is generated.
EDIT2: I obscured something else in my attempt to boil down the scenario: I can't (easily) change these old libraries as they are controlled by a different division of the company and are partially dynamic based on server-side logic (which again, I don't control). So as #ddotsenko suggested, maybe I should rephrase the question: how does one take some legacy scripts that depend on injecting script tags, and make them fit into the rest of the site that is trying to do the right thing by using AMD/RequireJS? The suggested shim approach comes close but doesn't work because the dependencies are unknown.
The problem is document.write() does not work after page load (and RequireJS uses XHR).
But you can use "shim" to predefine dependencies of your old modules - http://requirejs.org/docs/api.html#config-shim
config.js
require.config({
deps: ["main"],
shim: {
script1: {
deps: ['script2'],
exports: "someScript1Object"
}
}
});
main.js
require(
[ "script1" ],
function(someScript1Object) {
// ...
}
);
And console output is
script2
script1
UPDATE. Second variant: override document.write / document.writeln
You can use trick from ControlJS - overriding document.write. Try this code:
require.docwrite = function(text){
var aMatches = text.match(/src='([^']*)/i) ||
text.match(/src="([^"]*)/i) ||
text.match(/src=([^ >]*)/i);
if ( aMatches ) {
var url = aMatches[1];
require([ url ]);
}
}
document.write = require.docwrite;
document.writeln = require.docwrite;

JavaScript TinyMCE/jQuery race condition on firefox

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);
}
});

Categories