Currently I'm developing some web applications for internal use, porting from some old ones.
I'd love jQuery, the jQuery way to do asynchronous requests is brilliant. Then I developed a plugin for my apps. Basically, it "automagically" catches all clicks on links, sends an AJAH request and loads the response inside a container.
The problem came when the response contains <STYLE> or <LINK> tags. While using Firefox and Opera all was great. But, oh $h*t, no style was applied with IE6, IE7, Chrome or Safari. I was thinking that was my plugin's fault, but when I test the use case without it, the problem remains. No style applied.
Case test
Check this simplified case without even jQuery:
<body>
<p id="test">This test should have border and padding!</p>
<script type="text/javascript">
var div = document.createElement('div');
// <br/> added to avoid a bug in IE
div.innerHTML = '<br /><style>#test { border:2px solid #000; padding:1ex; }</style>';
var span = div.getElementsByTagName('span')[0];
document.body.appendChild(div);
</script>
</body>
I thought that jQuery have workarounds for this, but I found none, so I created a plugin to do the hard work.
Maybe I missed some jQuery functionality, I'm not a guru.
jQuery cross-browser inline style plugin
This plugin relies on detecting whether inlined styles are processed or not. If not, modify $.fn.html to move all style's and link's to document.head, mark them with a 'data-style-loader' attribute and adding a dummy link to the target. When te dummy link is removed (with the same $.fn.html function), it removes all related style's and link's from head.
Well, that's the code:
$(function($) {
// Checks for some supported features
(function() {
var div = document.createElement('div');
div.id = 'jquery-support-styled';
document.body.appendChild(div);
// Checks whether inlined styles applies automaticly
// (Firefox and Opera returns true)
div.innerHTML = '<style>#'+div.id+
' span { display:block; width:3px; }</style><span />';
var span = div.getElementsByTagName('span')[0];
$.support.inlineStyleApplies = $(span).width() == 3;
// Checks whether inlined styles applies if next to a 'br' (for IE)
$.support.mustPrependBrToInlineStyles =
!$.support.inlineStyleApplies && (function(){
div.innerHTML = '<br /><style>#'+div.id+
' span { display:block; width:5px; }</style><span />';
var span = div.getElementsByTagName('span')[0];
return ($.support.inlineStyleApplies = $(span).width() == 5);
})();
document.body.removeChild(div);
})();
// Saves old $().html function
var old_html = $.fn.html;
if($.support.mustPrependBrToInlineStyles) {
// Modify old $().html to add br before styles and links
$.fn.html = function(_value) {
if(typeof _value !== 'string') {
_value = _value.replace(/<style|<link/gi, function(_text) {
return '<br style="display:none"/>'+_text
})
}
return old_html.call(this, _value);
}
} else if(!$.support.inlineStyleApplies) {
// Change old $().html to move link´s and style´s to the head
$.fn.html = function(_text) {
// Remove css-pointers and their pointees
var $styles = this.find('link[data-style-loader]');
if($styles.length) { clean_css($styles); }
// Calls old $().html
old_html.call(this, _text);
// Promote new links and styles to document.head
$styles = this.find('style,link[rel*=stylesheet]');
if($styles.length) { add_styles.call(this, $styles) }
return this;
}
// Remove css-pointers and their pointees
function clean_css(_$styles) {
// Get pointees id
var st = [];
for(var i = 0; i < _$styles.length; ++i) {
st[st.length] = '[data-style-loader='+
$(_$styles[i]).attr('data-style-loader')+']'
}
// Remove all links pointed
$('head').find(st.join(', ')).remove();
}
// Promote new links and styles to document.head
function add_styles(_$styles) {
// Selects a pointer id
var tm = (new Date).getTime();
// Move to head and add attribute with pointer id
_$styles.attr('data-style-loader', tm).appendTo('head')
// Create a link inside the target with info to the new links and styles
this.append('<link data-style-loader='+tm+' />')
}
}
})
Tests and bugs
I have tested the plugin with these cases:
- <p>Default, no style</p>
- <style type="text/css">#test { color:red; }</style>
- <link rel="stylesheet" href="remote.css" /> and remote.css #test { color:red; }
- <style type="text/css">@import url("remote.css")</style>
- <link rel="stylesheet" href="import.css" /> and import.css @import url("remote.css")
The plugin worked GREAT! But... with some exceptions:
-
In IE6 and IE7, case 4 fails silently.
Surprisingly, IE5 is no problem. (WTF?)
-
In Safari and Chrome, the style isn't removed when loading 1) after 3) or 5).
Loading 1) after 2) or 4) works ok. WTF again??
Well, this browser war is almost won...
But... HOW can I lead my JS to the total victory???
2009-12-03 Still unsolved
No comments:
Post a Comment