Is there a reliable, cross-browser way to use polyfills with knockout? - javascript

We have a site that is driven pretty much entirely out of knockout and we need to support all major browsers, including Internet Explorer back to IE7 (not IE6).
Chrome already supports almost all of the HTML 5 features that we actually care about, and Modernizr handles the CSS hacks like a champ. But there are times when we still have to resort to polyfills, two notable examples being the placeholder attribute and more recently the <details> element.
Most of the polyfills are or rely on jQuery plugins, which is great in theory. Unfortunately, they also tend to be ineffective at dealing with dynamically loaded content - which there's a ton of when you use knockout (or any templating engine, really). Further complicating matters is that we're using knockout as true MVVM, so there's no decent place to shoehorn in a bunch of JS hacks to reload the plugins (which is probably a good thing as far as our architecture is concerned, but frustrating on this front).
We were able to come up with a semi-reliable implementation using the DOMNodeInserted event (deprecated, I know) for Firefox and IE9. Unfortunately it didn't work in IE8, because legacy IE doesn't support it and it seems damn near impossible to replicate in those browsers. onreadystatechange seemed promising at first but the event tends to fire too early - even if readyState is explicitly checked - and the polyfills miss their targets, so to speak.
The only option we tried that actually worked reliably in IE7/IE8 was using a repeating timeout to rerun the polyfills every 50 ms. Unfortunately, that also consumed an entire CPU constantly, and bumping it up to even 100 ms caused a too-noticeable delay in the UI, so not really suitable for production use.
So: Is there any reliable way of combining traditional polyfill techniques with dynamic content and templating engines like knockoutjs, that works in every major browser down to at least IE7?
(FWIW, we did eventually manage a workaround using knockout's afterRender binding, but that kind of takes the "poly" out of "polyfill". I'm hoping for something that we can write once and then forget about.)

The way I solved the same problem was to wrap most of my jQuery plugins, and behaviors in general, in knockout bindings (http://knockoutjs.com/documentation/custom-bindings.html). So I had, for example, a placeholder "binding" that I used on each input like <input data-bind="placeholder:'Some Placeholder Text'"/> that either simply set the placeholder attribute or did some IE hack depending on the need.
A broader solution would be to augment knockouts binding provider (http://www.knockmeout.net/2011/09/ko-13-preview-part-2-custom-binding.html). The binding provider is the thing that traverses the DOM (both on load and when dynamically loaded) and identifies bindings. By default, that essentially means it's just looking for data-bind attributes and ko comments, but you can change this to also find attributes like placeholder, input types for date or number inputs, etc, and add your IE hacks.

Related

What repercussions do I get by using an undefined HTML element?

In an effort to write more expressive HTML, I feel custom HTML elements are a good way for any webapp or document I may write to have good meaning gleamed from the tag name itself without the use of comments.
It appears I can define a custom HTML element with:
document.registerElement("x-el");
However it also appears that I can use a custom element before defining it:
<body>
<x-salamander>abc</x-salamander>
</body>
Or even:
<salamander>abc</salamander>
I suppose this is invalid HTML, however both Firefox and Chromium proceed to display the element without any problems or console warnings.
I can even execute the following with no complaints from the browser:
document.getElementsByTagName("salamander")[0]
Using this tag name as a selector in CSS also works fine. So, what problems might I face if I use undeclared elements in this way?
The problem with what you're trying to do is not that we can tell you it will break in some expected ways. It's that when you deviate from standards in this way, no one knows what to expect. It is, by definition, undefined, and the behavior of browsers that see it is also undefined.
That said, it might work great! Here's the things to keep in mind:
The HTMLUnknownElement interface is what you're invoking to make this work in a supported way - as far as I can tell in 5 minutes of searching, it was introduced in the HTML5 spec, so in HTML5 browsers that use it appropriately, this is no longer an undefined scenario. This is where registerElement comes into play, which can take an HTMLUnknownElement and make it known.
Browsers are typically very good at coping with unexpected markup... but it won't always result in great things (see: quirks mode).
Not all browsers are created equal. Chrome, Firefox, Safari, Opera, even IE will likely have some reliable way to handle these elements reliably (even pre-HTML5)... but I have no idea what a screen reader (Lynx) or various other esoteric, outdated, niche or even future browsers will do with it.
Everyone has said the same thing, but it's worth noting: you will fail validation. It's OK to have validation errors on your page so long as you know what they are and why they are there, and this would qualify, but you'd better have a good reason.
Browsers have a long history of taking whatever you give them and trying to do something reasonable with it, so you're likely to be OK, and if you are interested in primarily targeting HTML5 browsers, then you're very likely to be OK. As with everything HTML related, the only universal advice is to test your target demographic.
First problem I can see is that IE8 and lower will not apply your styling consistently. Even with "css resets", I get issues in IE8. It's important for the browser to know whether it's dealing with a block, inline block, list, etc, as many CSS behaviors are defined by the element type.
Second, I've never tried this, but if you use jQuery or another framework, I don't think they're built to handle non HTML tags as targets. You could create issues for your coders.
And HTML validators will probably have heart-attacks, so you lose a valuable tool.
You are re-inventing the wheel here. AngularJS has already solved the problem of adding HTML elements and attributes via what it calls directives:
Angular's HTML compiler allows the developer to teach the browser new
HTML syntax. The compiler allows you to attach behavior to any HTML
element or attribute and even create new HTML elements or attributes
with custom behavior. Angular calls these behavior extensions
directives.
The goal of Angular is broader in that it treats HTML as if HTML were a tool meant to build applications instead of just display documents. To me, this broader goal gives real meaning and purpose to the ability to extend HTML as described in your question.
You should use the namespaced version document.createElementNS instead of plain document.createElement. As you can see in the snippet below,
(...your custom element...) instanceof HTMLUnknownElement
will return false if you do that (it will be true when you do it unnamespaced)
I strongly suspect that validators won't even complain, because it's in your own namespace, and the validator (unless written by a stupid person) will (at least, really really really should) acknowledge that the 'namespaced stuff' is something it doesn't know enough about to condemn it.
New (formerly custom) elements arising in future HTML versions is a certain thing to happen, and it will happen even more often for namespaced elements compared to elements in the default namespace. And the 'HTML specs crowd' is simply not in charge of what, for example, the 'SVG spec crowd' will be doing next year or in 10. And which new namespaces will be introduced by god knows who and become common. They know they are not 'in charge of that', because they aren't stupid. For those reasons, you can bet your last shirt that you will not run into any serious problems (like errors being thrown or something of that sort) when you just go ahead and use them - it's OK if you're the first one. The worst thing that could possibly happen is that they don't look (aren't rendered) the way you'd wish, if you didn't write any CSS for them; anyway, the foremost use-case are probably invisible elements (you can be sure that display:none will work on your custom elements) and "transparent containers" (which won't effect the rest of the CSS unless you have ">" somewhere in the CSS). Philosophically, what you're doing is very much akin to jQuery using class names to better be able to transform the document in certain ways. And there is absolutely nothing wrong with jQuery doing that, and if the class in question is not referenced by some CSS, that does not make the slightest difference. In the same fashion, there is absolutely nothing wrong when you use custom elements. Just use the namespaced version. That way, you're also safe to use any names that might later be added to 'proper' HTML without causing any conflicts with how those elements later will be supposed to work.
And if - surprisingly - some validator does complain, what you should do is go on with your custom elements and ditch that validator. A validator complaining about how you use your very own namespace you just came up with is akin to a traffic cop visiting you at your home and complaining about the fashion in which you use your restroom - ditch it, got me?
bucket1 = document.getElementById('bucket1');
console1 = document.getElementById('console1');
bucket2 = document.getElementById('bucket2');
console2 = document.getElementById('console2');
chicken = document.createElement('chicken');
chicken.textContent = 'gaak';
bucket1.appendChild(chicken);
console1.appendChild(document.createTextNode([
chicken instanceof HTMLUnknownElement,
chicken.namespaceURI,
chicken.tagName
].join('\n')));
rooster = document.createElementNS('myOwnNSwhereIamKing', 'roosterConFuoco');
rooster.textContent = 'gaakarissimo multo appassionata';
bucket2.appendChild(rooster);
console2.appendChild(document.createTextNode([
rooster instanceof HTMLUnknownElement,
rooster.namespaceURI,
rooster.tagName
].join('\n')));
=====chicken=====<br>
<div id='bucket1'></div>
<pre id='console1'></pre>
=====rooster=====<br>
<div id='bucket2'></div>
<pre id='console2'></pre>
MDN article
plus, you've got almost universal browser support for createElementNS.
hmmm... just found out that if you use .createElementNS, the created elements don't have the dataset property. You can still use .setAttribute('data-foo', 'bar') but .dataset.foo='bar' would have been nicer. I almost feel like downvoting my own answer above. Anyway, I hereby frown upon the browser vendors for not putting in dataset.

jQuery vs document.querySelectorAll

I heard several times that jQuery's strongest asset is the way it queries and manipulates elements in the DOM: you can use CSS queries to create complex queries that would be very hard to do in regular javascript .
However , as far as I know, you can achieve the same result with document.querySelector or document.querySelectorAll, which are supported in Internet Explorer 8 and above.
So the question is this: why 'risk' jQuery's overhead if its strongest asset can be achieved with pure JavaScript?
I know jQuery has more than just CSS selectors, for example cross browser AJAX, nice event attaching etc. But its querying part is a very big part of the strength of jQuery!
Any thoughts?
document.querySelectorAll() has several inconsistencies across browsers and is not supported in older browsersThis probably won't cause any trouble anymore nowadays. It has a very unintuitive scoping mechanism and some other not so nice features. Also with javascript you have a harder time working with the result sets of these queries, which in many cases you might want to do. jQuery provides functions to work on them like: filter(), find(), children(), parent(), map(), not() and several more. Not to mention the jQuery ability to work with pseudo-class selectors.
However, I would not consider these things as jQuery's strongest features but other things like "working" on the dom (events, styling, animation & manipulation) in a crossbrowser compatible way or the ajax interface.
If you only want the selector engine from jQuery you can use the one jQuery itself is using: Sizzle That way you have the power of jQuerys Selector engine without the nasty overhead.
EDIT:
Just for the record, I'm a huge vanilla JavaScript fan. Nonetheless it's a fact that you sometimes need 10 lines of JavaScript where you would write 1 line jQuery.
Of course you have to be disciplined to not write jQuery like this:
$('ul.first').find('.foo').css('background-color', 'red').end().find('.bar').css('background-color', 'green').end();
This is extremely hard to read, while the latter is pretty clear:
$('ul.first')
.find('.foo')
.css('background-color', 'red')
.end()
.find('.bar')
.css('background-color', 'green')
.end();
The equivalent JavaScript would be far more complex illustrated by the pseudocode above:
1) Find the element, consider taking all element or only the first.
// $('ul.first')
// taking querySelectorAll has to be considered
var e = document.querySelector("ul.first");
2) Iterate over the array of child nodes via some (possibly nested or recursive) loops and check the class (classlist not available in all browsers!)
//.find('.foo')
for (var i = 0;i<e.length;i++){
// older browser don't have element.classList -> even more complex
e[i].children.classList.contains('foo');
// do some more magic stuff here
}
3) apply the css style
// .css('background-color', 'green')
// note different notation
element.style.backgroundColor = "green" // or
element.style["background-color"] = "green"
This code would be at least two times as much lines of code you write with jQuery. Also you would have to consider cross-browser issues which will compromise the severe speed advantage (besides from the reliability) of the native code.
If you are optimizing your page for IE8 or newer, you should really consider whether you need jquery or not. Modern browsers have many assets natively which jquery provides.
If you care for performance, you can have incredible performance benefits (2-10 faster) using native javascript:
http://jsperf.com/jquery-vs-native-selector-and-element-style/2
I transformed a div-tagcloud from jquery to native javascript (IE8+ compatible), the results are impressive. 4 times faster with just a little overhead.
Number of lines Execution Time
Jquery version : 340 155ms
Native version : 370 27ms
You Might Not Need Jquery provides a really nice overview, which native methods replace for which browser version.
http://youmightnotneedjquery.com/
Appendix: Further speed comparisons how native methods compete to jquery
Array: $.inArray vs Array.indexOf: Jquery 24% slower
DOM: $.empty vs Node.innerHtml: Jquery 97% slower
DOM: $.on vs Node.addEventListener: Jquery 90% slower
DOM: $.find vs Node.queryselectorall: Jquery 90% slower
Array: $.grep vs Array.filter: Native 70% slower
DOM: $.show/hide vs display none: Jquery 85% slower
AJAX: $.ajax vs XMLHttpRequest: Jquery 89% slower
Height: $.outerHeight vs offsetHeight: Jquery 87% slower
Attr: $.attr vs setAttribute: Jquery 86% slower
To understand why jQuery is so popular, it's important to understand where we're coming from!
About a decade ago, top browsers were IE6, Netscape 8 and Firefox 1.5. Back in those days, there were little cross-browser ways to select an element from the DOM besides Document.getElementById().
So, when jQuery was released back in 2006, it was pretty revolutionary. Back then, jQuery set the standard for how to easily select / change HTML elements and trigger events, because its flexibility and browser support were unprecedented.
Now, more than a decade later, a lot of features that made jQuery so popular have become included in the javaScript standard:
Instead of jQuery's $(), you can now now use Document.querySelectorAll()
Instead of jQuery's $el.on(), you can now use EventTarget.addEventListener()
Instead of jQuery's $el.toggleClass(), you can now use Element.classList.toggle()
...
These weren't generally available back in 2005. The fact that they are today obviously begs the question of why we should use jQuery at all. And indeed, people are increasingly wondering whether we should use jQuery at all.
So, if you think you understand JavaScript well enough to do without jQuery, please do! Don't feel forced to use jQuery, just because so many others are doing it!
That's because jQuery can do much more than querySelectorAll.
First of all, jQuery (and Sizzle, in particular), works for older browsers like IE7-8 that doesn't support CSS2.1-3 selectors.
Plus, Sizzle (which is the selector engine behind jQuery) offers you a lot of more advanced selector instruments, like the :selected pseudo-class, an advanced :not() selector, a more complex syntax like in $("> .children") and so on.
And it does it cross-browsers, flawlessly, offering all that jQuery can offer (plugins and APIs).
Yes, if you think you can rely on simple class and id selectors, jQuery is too much for you, and you'd be paying an exaggerated pay-off. But if you don't, and want to take advantage of all jQuery goodness, then use it.
jQuery's Sizzle selector engine can use querySelectorAll if it's available. It also smooths out inconsistencies between browsers to achieve uniform results. If you don't want to use all of jQuery, you could just use Sizzle separately. This is a pretty fundamental wheel to invent.
Here's some cherry-pickings from the source that show the kind of things jQuery(w/ Sizzle) sorts out for you:
Safari quirks mode:
if ( document.querySelectorAll ) {
(function(){
var oldSizzle = Sizzle,
div = document.createElement("div"),
id = "__sizzle__";
div.innerHTML = "<p class='TEST'></p>";
// Safari can't handle uppercase or unicode characters when
// in quirks mode.
if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
return;
}
If that guard fails it uses it's a version of Sizzle that isn't enhanced with querySelectorAll. Further down there are specific handles for inconsistencies in IE, Opera, and the Blackberry browser.
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id === match[3] ) {
return makeArray( [ elem ], extra );
}
} else {
return makeArray( [], extra );
}
And if all else fails it will return the result of oldSizzle(query, context, extra, seed).
In terms of code maintainability, there are several reasons to stick with a widely used library.
One of the main ones is that they are well documented, and have communities such as ... say ... stackexchange, where help with the libraries can be found. With a custom coded library, you have the source code, and maybe a how-to document, unless the coder(s) spent more time documenting the code than writing it, which is vanishingly rare.
Writing your own library might work for you , but the intern sitting at the next desk may have an easier time getting up to speed with something like jQuery.
Call it network effect if you like. This isn't to say that the code will be superior in jQuery; just that the concise nature of the code makes it easier to grasp the overall structure for programmers of all skill levels, if only because there's more functional code visible at once in the file you are viewing. In this sense, 5 lines of code is better than 10.
To sum up, I see the main benefits of jQuery as being concise code, and ubiquity.
Here's a comparison if I want to apply the same attribute, e.g. hide all elements of class "my-class". This is one reason to use jQuery.
jQuery:
$('.my-class').hide();
JavaScript:
var cls = document.querySelectorAll('.my-class');
for (var i = 0; i < cls.length; i++) {
cls[i].style.display = 'none';
}
With jQuery already so popular, they should have made document.querySelector() behave just like $(). Instead, document.querySelector() only selects the first matching element which makes it only halfway useful.
Old question, but half a decade later, it’s worth revisiting. Here I am only discussing the selector aspect of jQuery.
document.querySelector[All] is supported by all current browsers, down to IE8, so compatibility is no longer an issue. I have also found no performance issues to speak of (it was supposed to be slower than document.getElementById, but my own testing suggests that it’s slightly faster).
Therefore when it comes to manipulating an element directly, it is to be preferred over jQuery.
For example:
var element=document.querySelector('h1');
element.innerHTML='Hello';
is vastly superior to:
var $element=$('h1');
$element.html('hello');
In order to do anything at all, jQuery has to run through a hundred lines of code (I once traced through code such as the above to see what jQuery was actually doing with it). This is clearly a waste of everyone’s time.
The other significant cost of jQuery is the fact that it wraps everything inside a new jQuery object. This overhead is particularly wasteful if you need to unwrap the object again or to use one of the object methods to deal with properties which are already exposed on the original element.
Where jQuery has an advantage, however, is in how it handles collections. If the requirement is to set properties of multiple elements, jQuery has a built-in each method which allows something like this:
var $elements=$('h2'); // multiple elements
$elements.html('hello');
To do so with Vanilla JavaScript would require something like this:
var elements=document.querySelectorAll('h2');
elements.forEach(function(e) {
e.innerHTML='Hello';
});
which some find daunting.
jQuery selectors are also slightly different, but modern browsers (excluding IE8) won’t get much benefit.
As a rule, I caution against using jQuery for new projects:
jQuery is an external library adding to the overhead of the project, and to your dependency on third parties.
jQuery function is very expensive, processing-wise.
jQuery imposes a methodology which needs to be learned and may compete with other aspects of your code.
jQuery is slow to expose new features in JavaScript.
If none of the above matters, then do what you will. However, jQuery is no longer as important to cross-platform development as it used to be, as modern JavaScript and CSS go a lot further than they used to.
This makes no mention of other features of jQuery. However, I think that they, too, need a closer look.
as the official site says:
"jQuery: The Write Less, Do More, JavaScript Library"
try to translate the following jQuery code without any library
$("p.neat").addClass("ohmy").show("slow");
I think the true answer is that jQuery was developed long before querySelector/querySelectorAll became available in all major browsers.
Initial release of jQuery was in 2006. In fact, even jQuery was not the first which implemented CSS selectors.
IE was the last browser to implement querySelector/querySelectorAll. Its 8th version was released in 2009.
So now, DOM elements selectors is not the strongest point of jQuery anymore. However, it still has a lot of goodies up its sleeve, like shortcuts to change element's css and html content, animations, events binding, ajax.
$("#id") vs document.querySelectorAll("#id")
The deal is with the $() function it makes an array and then breaks it up for you but with document.querySelectorAll() it makes an array and you have to break it up.
Just a comment on this, when using material design lite, jquery selector does not return the property for material design for some reason.
For:
<div class="logonfield mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" id="myinputfield" required>
<label class="mdl-textfield__label" for="myinputfield">Enter something..</label>
</div>
This works:
document.querySelector('#myinputfield').parentNode.MaterialTextfield.change();
This does not:
$('#myinputfield').parentNode.MaterialTextfield.change();

What are the likely main reasons my website is very slow on IE?

I need to know what can be the main reasons (apart from the basics like grouping CSS selectors, reducing image size, using image sprite etc.) which makes a website slow on Internet Explorer, because my website works fine on the others like FF, chrome etc.
Is it the huge use of Javascript framework (ie. jQuery, extjs, prototype)?
Is it because of the use of plugins based on JS framework?
Should I use core javascript and remove the use of any js framework?
Should I try to avoid using jQuery(document).ready()? in case of jQuery framework?
Above some of the questions which I know and please answer the questions which I couldn't ask because of lesser knowledge about these.
I need to make my website perform well on IE (6,7,8) also please suggest.
Thanks
It has nothing to do with jQuery. The plugins however are hit or miss, and may not be well tested in IE. I'd use at your own risk.
DOM manipulation is very slow in IE. using appendChild (or insertRow) to add multiple nodes (say, 100+ for a long list) is much slower than building a string and doing one innerHTML.
You also want to be careful how you access nodes. Devs tend to rely upon jQuery too much and search for nodes via their class names, like:
$(".evenRows").hover(doSomething);
IE doesn't have a native way of getting a node by class name, so JQ is looping through the entire document and every single element and checking its class name... which needs to be checked via RegExp because it may look like:
class="evenRows yellow foo bar"
Finally, in spite of its improvements, IE8 is still using an old rendering engine - the same as IE6. Don't go crazy with the animations, and don't expect miracles.
As MSIE has a default-limit of 2 simultaneous connections you should minimize the number of requests that are required for building the page(use css-sprites, merge js-and css-files into a single file)
While you need to speed things up in IE, you can still use Firebug to look for places, that consume resources.
Install Yslow and see what it tells you
Run the site under profiler (Yslow or Firebug have one) and look for a bottleneck
It is very difficult to answer general questions like this, but jQuery is unlikely to be the one slowing everything down, just remember to
Use IDs as selectors wherever possible — they are the fastest, i.e. $('#myid')
Avoid using .class selectors without tagname, i.e. $('div.myclass') can be ten times faster than $('.myclass').
and so on
More tips for using jQuery to achieve better performance.
Earlier versions of IE will, in general, run JavaScript slower than later versions of IE, because there have been advances in JavaScript compilation speed since then.

Dynamically added AJAX content with wrong markup

i am using jQuery to dynamically add content
$("#articles").prepend('<article><header><p>info</p><h2>You are using Internet Explorer</h2></header><p>It is recommended that you use a modern browser like Firefox, Chrome or install Google Chrome Frame to experience better performance and advanced HTML5 and CSS3 features.</p></article>');
but the HTML i got was
notice the />
jQuery is using innerHTML, which doesn't always work with HTML5 elements even when the normal ‘shiv’ is in use. You would need another extra workaround hack, eg this.
I really don't think the proposed new HTML5 elements are ready for real-world use. They get you no practical gain yet, aren't even finalised, and cause a bunch of problems (working around which can be fragile and cost performance).
They don't really add anything semantic to your warning markup, and you're only ever showing it to IE anyway—the browser that can handle them least well of all.

CSS2 selectors and IE

I'd like to use CSS2 selectors ( parent > child, element:first-child, etc) in my stylesheet but IE6 doesn't seem to recognize those. Is there any plugin (jQuery preferably) that would allow me to use pseudo-selectors freely without worrying about the damned IE6?
UPDATED:
The Super Selectors jQuery plugin scans the page's styles for selectors which aren't supported by all browsers and then adds apropriate classes to elements that those CSS3 selectors are targeting.
You can also look at this CSS3 selectors for IE5/IE8 called ie-css3.js
I'd recommend against using javascript to fix those kind of problems.
The best approach I've found is using conditional comments and create a IE only CSS file, optimized for that hellish browser.
In the long run, the small duplication of work is compensated by the smaller amount of incompatibilities that you'd have to correct between sane browsers and IE.
As a rule, I go with adding classNames to the body tag via conditional comments as my preferred method to deal with cross-browser difficulties.
However, if that's out and if performance is not a concern, you can always give Dean Edwards's excellent IE7.js a try. It will parse and grok your stylesheet, picking out and implementing those unsupported selectors.
Beware that, as your stylesheets increase and size and complexity, the script's (and your site's) performance will suffer in IE6. However, in a lot of cases things should run just fine. Make sure to conditionally comment it in for IE6 and below and you'll be set.

Categories