How to know when font-face has been applied - javascript

I am currently building a corporate website for a customer that uses custom fonts extensively.
On jQuerys DOM-ready I am doing placement calculations to figure out where some pop-up menus with dynamic width and height should be placed based on their dynamic contents.
These calculations fail, since DOM-ready is fired before font-face is applied, and thus widths and heights are incorrect.
Right now (for the prototype) i am doing the calculations 500ms after DOM-ready to alleviate this problem, but this can't go into production for obvious reasons.
The problem has been observed in latest Firefox and chrome. IE 8 doesn't seem to have the problem, but then DOM-ready fires fairly late, so the delay is kind of built in I guess :)
Waiting for the load event is not an option, so my question to you is this:
Is there a reliable cross-browser way to detect when font-face has been applied?

I found a solution after wondering why IE doesn't suffer from this problem.
Firefox and Chrome/Safari triggers the DOMContentLoaded event before font-face is applied, thus causing the problem.
The solution is to not listen for DOMContentLoaded but instead go oldschool and listen to onreadystatechange and wait until the document.readyState === 'complete' which is always triggered after font-face is applied (as far as I can tell by my tests) - which is of course what always happens in IE since it doesn't support DOMContentLoaded.
So basically you can roll-your-own event in jQuery called fontfaceapplied - maybe it should be built in ;)
document.onreadystatechange = function() {
if (document.readyState === 'complete')
$(document).trigger('fontfaceapplied');
};
Funny fact: Opera does it right and waits to trigger DOMContentLoaded until font-face is applied.

ES6 update:
The question post is for many years ago which the IEs version 8 and earlier were still alive and even Ecmascript version 6 was not released, but now you can write callbacks on document.fonts events. eg:
document.fonts.onloadingdone = () => {
// do something after all fonts are loaded
};
For more information see this post.

Setting the function to trigger after a timeout of 200ms solves this issue when using Google Fonts.
There's a noticeable jump, but there usually is for equal heights stuff, for the purists this might not be perfect but it works cross browser.

Related

What is the best way to detect an image download completion - onload or addEventListener()?

For detecting when an image has completed downloading which method should I use?
image.onload = function () {}
or
image.addEventListener("load", function () {} );
onload:
Supports only a single listener.
Works in all browsers.
You unbind the event handler by clearing the onload property.
addEventListener:
Supports multiple listeners.
Doesn't work in older IE browsers (they use attachEvent).
You unbind the listener with removeEventListener() which requires info identifying the original eventListener.
If addEventListener is supported and you only need one listener, then you can use either.
If it's a simple self-contained piece of code that nobody else will be messing with then, there's no issue with using onload. If it's a more complex piece of software that other developers may mess with and any kind of extensibility is desired and you have cross browser support for event listeners, then addEventListener() is more flexible and is probably more desirable.
image.addEventListener("load", function() {} ); will not work on all browsers, but assuming you have a backup and use image.attachEvent("onload", function() {})
if(image.addEventListener) { image.addEventListener("load",function() {}); }
else { image.attachEvent("onload", function() {}); }
This will have the benefit that you are not overriding any onload event that already exists on the image, and that your onload event will not be overridden by any other code.
Directly modifying the "onload" attribute of a DOM Element is usually not recommended just because you may think "Hey, I can save a couple lines of code by setting it, and I only need one listener, so I'll just set it rather than using addEventListener/attachEvent" - but it invariably leads to "Well what if I need to add something else later?"
For this reason javascript developers usually use a library to attach events, so you can add the listener confidently with one line of code and it will work in all browsers.
try
if(image.complete){
//....
}
Both, Image.complete as well as image.onload don't seem to work in Webkit, at least the version on my mobile. They return false infinitely. It works in opera though, so the code may be ok.
Therefore I am currently using a trick that works at least in opera and webkit. I have a 1x1 pixel placeholder image ready. Now I set the src to the to be loaded image and (usually a bad practice, but this is a game that cannot start without that image) I go into a loop that updates some "loading" splashscreen gfx, but basicly just checks the width of the image, whether the image.width is bigger than 1 pixel.
Like I said, tested only with opera and webkit. I use it to load an atlas image and copy the containing tiles to individual images over a canvas. Once finished, the src of the atlas image is set back to the 1x1 placeholder, so it can be used that way again. For complete savity, one should also wait until the placeholder is 1 pixel in width again, before reuseing it that way.
const handleImage=()=>{}
image.addEventListener("load",handleImage); //adding a eventListener
image.removeEventListener("load",handleImage); //removeing a eventListener

Javascript: how to check if data is loaded

I'm developing a Chrome plugin. It injects a class name to every tag.
I have some problems with webpages such as facebook in which content is loaded afterwards when you scroll down.
I'd like to know if there a way to check if new content is loaded.
By now the only solution I could find is a
setInterval(function() {
Thanks.
There is a DOMSubtreeModified event (source) that Chrome supports - see this answer for details. Your code should look something like this:
document.addEventListener('DOMSubtreeModified', function() {
$("*:not(.my_class)").addClass('my_class');
}, true);
As Konrad Dzwinel said, you can use some Mutation Event listener
document.addEventListener("DOMSubtreeModified", methodToRun);
But note that the Mutation Events are performance hogs which can't really be tamed well (they fire too often and slow down the page a lot). Therefore, they have been deprecated over a year ago and should be used only when really needed. However, they work.
If you want this for a Chrome extension, you could use the new and shiny Mutation Observers from DOM Level 4 (follow the links there, they explain a lot!). Where DOMSubtreeModified fired a thousand times, MutationObserver fires only once with all the modifications contained and accessible.
Works for (as of 2012/06):
Chrome 18+ (prefixed, window.WebKitMutationObserver)
Firefox 14+ (unprefixed)
WebKit nightlies

svg animation does not start when loaded with settimeout

I'm loading 2 embedded svg-lets in a page. One animated one not. They're loaded in sequence with a setTimeout.
When I load the animated first all goes well, the animation starts as expected and the second static svg is correctly displayed afterwards. When I first load the second, and afterwards the animated one, the animation does not start.
Code is here: jsfiddle switch #svg1, and #svg2 in the javascript.
Upon browser checking I found out this is probably a webkit bug as chrome and safari both show this behavior FF 12 and Opera are well.
Can this be fixed with JS code or should I file a bug with webkit?
== Added
I think the question should be rephrased why the animation does not start after the svg is loaded with a settimeout.
As Jared investigated below it works when the element is present in the DOM and is reordered via dom manipulation into the focus element, Chrome and webkit need a kick with a beginElement() call to the animate element. This still doesn't work out for elements constructed from plain text. As I only do have a mac and I still consider this a hobby project I leave MS IE completely out of the loop.
Well, it took quite a bit longer than I anticipated, but I got it worked out. Basically, the method you were using with the semi-SVG and the regex on the markup, etc., was to say the least not quite the way to get it done.
The answer is to use svg DOM animation methods and attributes, especially to start/stop the animation when you want it to run. Apparently, Firefox was just fine with reinitializing the element and the animation just by manipulating the inner HTML/markup. Chrome (Webkit?), however, actually needs you to programmatically access and control the element. I have not checked in IE, Opera or Safari.
I redid the example, leaving out the arrow altogether, as it is unrelated to the problem. I instead concentrated on creating and testing the animation functionality. The critical points you were missing before were:
var $lasso = $('#lasso'),
animater = $lasso.find('animate')[0],
...
animater.beginElement();
...
animater.endElement();
Here is the demo I made, which is significantly different that what you have in your question:
http://jsfiddle.net/9hBfs/
What I call the "lasso" effect is still there, though.
I would reference the Mozilla Developer Network (MDN) site, as they have a lot of great information and are a highly trusted authority:
https://developer.mozilla.org/en/SVG
https://developer.mozilla.org/en/SVG/Element/animate
http://www.w3.org/TR/SVG11/animate.html#animation-mod
http://www.w3.org/TR/SVG11/animate.html#InterfaceElementTimeControl
http://www.w3.org/TR/SVG11/animate.html#RestartAttribute

Is it okay to call canvas.drawWindow() from the "onload" handler?

The note at the bottom of this Mozilla wiki page currently says: "Using canvas.drawWindow() while handling a document's onload event doesn't work. In Firefox 3.5 or later, you can do this in a handler for the MozAfterPaint event to successfully draw HTML content into a canvas on page load." Which is fine, except that I tried it in Firefox 3.6.6 and it did work, leading me to believe that perhaps it used to not work, due to some bug which has since been fixed. I'd rather not use MozAfterPaint since it won't work in versions earlier than 3.5. Is there an important reason not to use the "load" event, and if so what can I do instead that will be compatible with older versions of Firefox?
EDIT: This is how my code works. In the init() function of my extension, I call gBrowser.addEventListener("load", MyExtension.onPageLoad, true); Then MyExtension.onPageLoad is essentially:
onPageLoad : function(e) {
var win = e.originalTarget.defaultView;
// create an html:canvas, adjust its size, etc. following the example of the "TabPreview" extension
var ctx = canvas.getContext("2d");
ctx.drawWindow(win, 0, 0, w, h, "rgb(255, 255, 255");
// add the canvas to the DOM
},
I expect it's not a case of won't work "at all", but rather a case of won't work "correctly".
Traditionally, onload ran when the html of the page was finished loading. The CSS and scripting would happen later, or possibly at the same time 1.
This is why the MozAfterPaint was introduced. It lets you inject code after Gecko has enough information to render the page.
You might be able work around the absence of MozAfterPaint, by listening for DOM mutation events. It's not as clean, but I think it will work if you use it to clear and reset a 100-200 ms tineout. That shouldn't cause too much of a performance hit. When the timeout finally expires you know the page has been stable for at least that long.
[1] I spent a week chasing a global variable that went from undefined to defined during the execution of the onload handler. It turned out to be a script tag pulled in a JS file that had some top-level code and it was executing in parallel with the onload handler.
Looks like the Mozilla documentation is wrong, and it is okay to call canvas.drawWindow() from the "load" event.

Event listeners on plugin in document.onload events in Opera

I am trying to understand an issue where event-listener registration on plugins doesn't work in Opera unless I delay them.
In particular, this doesn't work:
document.onload = function() {
plugin.addEventListener("foo", function() { alert('onFoo'); }, false);
}
while delaying the addEventListener() call somewhat through e.g. an alert() does:
document.onload = function() {
alert('onload()');
plugin.addEventListener("foo", function() { alert('onFoo'); }, false);
}
It seems that plugins are only loaded after document.onload.
As a non-web-developer, am I missing something simple here? Or is this a known Opera problem with a common work-around?
in general the timing of plugin initialisation, script execution and document event handling isn't well specified, meaning browsers are likely to do different things.
In this case it sounds like you need to make sure the plugin is initialised before you add the listener. One way to do that would be to check for a property the plugin will define (for example, if it was a Flash plugin you could check if PercentLoaded was defined to see if it is ready for scripting.) If not ready for scripting, you could use a timeout to try again a little bit later.
At Opera we've been trying to align with the majority of the other browsers in this area recently, and Opera 10.50 may be working better for you. I'm not sure if we have things fully under control yet though - it would be interesting to hear from you whether behaviour changed in 10.50.
We have further improved handling of this in Opera 10.60, so that behavior is much closer to the other browsers wrt. plug-in initialization and script readyness. I believe the original approach should work now.
I don't know much about Opera but have you tried using jquery's ready function? It's purpose is to add a function you want executed once the DOM is fully loaded and it should work cross browser.
$(document).ready(function() {
plugin.addEventListener("foo", function() { alert('onFoo'); }, false);
});
More info about the ready function can be found here

Categories

Resources