After importing babel-polyfill in my entry point to Browserify with a babel transformation, IE11 is still complaining about Object.assign. In addition to Object.assign my project is using a number of other new APIs like Number.isNan, HTMLElement.contains, KeyboardEvent.key, etc.
I cannot seem to find any documentation on what polyfills are added via this plugin. Does anyone know what APIs are polyfilled by this plugin or where I can find a comprehensive list? All I could find was this sentence:
"This will emulate a full ES6 environment"
Which does not seem to be the case as Object.assign is still undefined.
Looking at the source on github it does the string padding methods and the array methods. In other words, the quote you referenced is marketing-speak. Use another polyfill for the stuff you want. Its not terribly difficult to polyfill a lot of that stuff, e.g.
Number.isNaN = Number.isNaN || function(n) { return n !== n; };
From MDN
if (typeof Object.assign != 'function') {
(function () {
Object.assign = function (target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
})();
}
When looking at the source of babel-polyfill, it's there:
// 19.1.3.1 Object.assign(target, source)
var $export = _dereq_(33);
$export($export.S + $export.F, 'Object', {assign: _dereq_(66)});
},{"33":33,"66":66}],178:[function(_dereq_,module,exports){
var $export = _dereq_(33)
Which version of babel are you using? And are you sure you included the correct babel plugins in browserify?
Related
How to check if the class URL() is supported in the current browser?
Based on the docs it's not supported in IE
I want to use it to get a domain out of a string like that:
var text = ...
var domain = new URL(text).host;
You could do a feature check with
if ("URL" in window)
However this won't validate whether the functionality is correct. you might want to consider adding a polyfill.
Note that IE/Edge seem to really make built in constructors as objects, meaning typeof Ctor === "object" is true in those browsers. So if they add support in Edge for it, the checks for "function" will be invalid.
You can't do this with complete reliability. You could come close by testing if URL exists and is a function:
if (typeof URL !== "function") {
// It is not supported
}
You could then perform further tests to see how much it looks like a duck:
function URL_is_supported() {
if (typeof URL !== "function") {
return false;
}
// Beware: You're calling the function here, so it it isn't the expected URL function it might have undesired side effects
var url = new URL("http://example.com");
if (url.hostname !== "example.com") {
return false;
}
// and whatever other tests you wanted to do before you're convinced
return true;
}
to check if anything is supported on a root level (window) just try to access it on a condition level. E.G.
(window.URL) OR JUST (typeof URL === "function")
var a = window.URL ? window.URL(text).host : ....
also remembering that the fact of window has a "URL" property doesn't mean that it is a class/function and that it is what you expect
so the best approach would be check using the typeof version which at least guarantee that it is a function
The closest you can get to check if URL is truly supported is checking its prototype and static functions
function isURLSupported(){
if(typeof window.URL!=="function" || typeof URL.createObjectURL !== "function" || typeof URL.revokeObjectURL !== "function"){
return false;
}
var oURLprototype= ["host","hostname","href","origin","password","pathname","port","protocol","search","searchParams","username"];
for(var i=0;i<oURLprototype.length;i++){
if(URL.prototype.hasOwnProperty(oURLprototype[i])===undefined){
return false;
}
}
return true;
}
For those who will support implementations that have classes as type Object not Function
--Is function credit to https://stackoverflow.com/a/7356528/835753
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
function isURLSupported(){
if(!isFunction(window.URL) || !isFunction(URL.createObjectURL) || !isFunction(URL.revokeObjectURL)){
return false;
}
var oURLprototype= ["host","hostname","href","origin","password","pathname","port","protocol","search","searchParams","username"];
for(var i=0;i<oURLprototype.length;i++){
if(URL.prototype.hasOwnProperty(oURLprototype[i])===undefined){
return false;
}
}
return true;
}
I use a prototype in my project:
NodeParser.prototype.getChildren = function(parentContainer) {
return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {
var container = [node.nodeType === Node.TEXT_NODE && !(node.parentNode instanceof SVGElement) ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);
return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== "TEXTAREA" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;
}, this));
};
We have to add our client javascript file to our project. They have a code like this:
Array.prototype.map = function(fnc) {
//code block
}
map in our code, return to them Array.prototype.map . How could I prevent such conflict?
This conflict happens in local only. In production there is no any conflict problem.
The only bullet proof solution would be to ask them not to monkeypatch prototypes of native object. Or at least do it in spec conformant way if they were doing it to polyfill native methods in older browsers.
if(typeof Array.prototype.map !== 'function') {
Array.prototype.map = function mapPolyfil() {
};
}
If this is not an option due to some contract obligations. You have the following options:
You can save native versions of monkeypatched methods before they did it.
var safeMap = Function.bind.call([].map);
// usage
safeMap(myArray, callback, thisArg)
If they "only" missed thisArg in their map implementation you can make sure you always pass prebinded functions
array.map(function(){}.bind(this))
Or even monkeypatch their implementation to start Eternal Monkeypatchers War
if(Array.prototype.map.length === 1) { //was patched
var theirMap = Array.prototype.map;
Array.prototype.map = function(fn, thisArg) {
if(arguments.length === 2) {
fn = fn.bind(thisArg)
}
return theirMap.call(this, fn);
}
}
Yes, I know that. Function bind is not supported by Phantomjs. But maybe I can use something else, or say page.open not to use bind? It seems to be OK, but some websites
return error
TypeError: 'undefined' is not a function (evaluating 'b.bind(a)')
After that I wrote a simple script, which just opens a page:
var address = phantom.args[0];
if(!address) phantom.exit(1);
page = require("webpage").create();
page.open(address, function(status){
setInterval(
function () {
console.log(
page.evaluate(function() {
return document.body.innerHTML.length;
})
)}, 200)
})
But error is still there. Error is not the main problem, but the problem is to get page content, because after error page content is not loading...
So, I need help.
P.S. Problem website is http://vkrushelnytskiy.wix.com/aaaa
There is an npm package which loads a polyfill for phantomJS's missing .bind method. Copying the installation instructions here for convenience, but follow the link for any updates.
Installation
npm install --save-dev phantomjs-polyfill
Usage
require('phantomjs-polyfill')
Usage with Karma
Include the polyfill directly in the files list of your karma.conf
...
files: [
'./node_modules/phantomjs-polyfill/bind-polyfill.js',
...
]
...
You can use this code as an alternative
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
You can shim Function.bind using the following polyfill.
Just prepend it to the code you are trying to run. There are probably nicer solutions, but this worked great for me.
You can mention this bind function just before the test case.
// PhantomJS doesn't support bind yet
Function.prototype.bind = Function.prototype.bind ||
function (ctx) {
var fn = this,
args = [],
param_length = 0;
for(var i=0; i<arguments.length; i++) {
if(i){
args[i-1] = arguments[i];
}
}
param_length = args.length;
return function () {
for(var i =0; i<arguments.length; i++){
args[param_length + i] = arguments[i];
}
return fn.apply(ctx, args);
};
};
SIMPLIFIED EXAMPLE CODE:
var $ = function(selector, node) { // Selector engine
var selector = selector.trim(), node = node || document.body;
if (selector != null) {
return Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
}
}
I want to use it like this...:
$("div").innerHTML='It works!';
...not like this...:
$("div")[0].innerHTML='It works only on the specified index!';
...or this:
for(i=0;i<$('div');i++) {
$("div")[i].innerHTML='It works great but it's ugly!';
}
This is as close as I got. I would like chaining to work and for it to be compatible with native methods:
if(!Array.prototype.innerHTML) {
Array.prototype.innerHTML = function(html) {
for (var i = 0; i < this.length; i++) {
this[i].innerHTML = html;
}
}
}
$("div").innerHTML('It works, but it ruins method chaining!');
I decided to build this engine to better learn JavaScript; It's working but I am hoping I can learn some more from the kind members of Stack Overflow. Any help would be much appreciated!
I want to use it like this...:
$("div").innerHTML='It works!';
...not like this...:
$("div")[0].innerHTML='It works only on the specified index!';
It sounds like you want to have assigning to innerHTML on your set of results assign to the innerHTML of all of the results.
To do that, you'll have to use a function, either directly or indirectly.
Directly:
var $ = function(selector, node) { // Selector engine
var selector = selector.trim(),
node = node || document.body,
rv;
if (selector != null) {
rv = Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
rv.setInnerHTML = setInnerHTML;
}
return rv;
}
function setInnerHTML(html) {
var index;
for (index = 0; index < this.length; ++index) {
this[index].innerHTML = html;
}
}
// Usage
$("div").setInnerHTML("The new HTML");
There, we define a function, and we assign it to the array you're returning as a property. You can then call that function on the array. (You might want to use Object.defineProperty if it's available to set the setInnerHTML property, so you can make it non-enumerable.)
Indirectly (requires an ES5-enabled JavaScript engine):
var $ = function(selector, node) { // Selector engine
var selector = selector.trim(),
node = node || document.body,
rv;
if (selector != null) {
rv = Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
Object.defineProperty(rv, "innerHTML", {
set: setInnerHTML
});
}
return rv;
}
function setInnerHTML(html) {
var index;
for (index = 0; index < this.length; ++index) {
this[index].innerHTML = html;
}
}
// Usage
$("div").innerHTML = "The new HTML";
There, we use Object.defineProperty to define a setter for the property.
In the comments below you say
I have a few prototypes that work when individually attached to the $ function. Example: $('div').makeClass('this'); They do not work when they are chained together. Example: $('div').makeClass('this').takeClass('that');
To make chaining work, you do return this; from each of the functions (so the end of makeClass would do return this;). That's because when you're chaining, such as obj.foo().bar(), you're calling bar on the return value of foo. So to make chaining work, you make sure foo returns this (the object on which foo was called).
This is what works; it's a slightly different syntax then I gave in my prior example, but the end result is the same. I had some great help from other Stack Exchange members, thanks again everyone.
var $ = function(selector, node) { // Selector engine
var selector = selector.trim(), node = node || document.body;
if (selector != null) {
return Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
}
}
if(!Array.prototype.html) {
Array.prototype.html = function(html) {
for (var i = 0; i < this.length; i++) {
this[i].innerHTML = html;
}
return this; //<---- Silly me, my original code was missing this.
}
}
When I run it, everything (including chaining) works as desired:
$("div").html('hello world');
OUTPUT:
<div>hello world</div>
Cheers!
Looks like I've re-invented the wheel, but somehow this isn't working in Internet Explorer 9, but does in IE6.
function debug()
if(!window.console) {
window.console = { log: function() { /* do something */ } };
}
console.log.apply(console, arguments);
}
Related:
Apply() question for javascript
F12 Debugger tells me that this "object" (console.log) does not support method 'apply'.
Is it not even recognized as a function?
Any other pointers or ideas?
The second part of an answer I gave recently answers this question too. I don't consider this a duplicate of that one so, for convenience, I'll paste it here:
The console object is not part of any standard and is an extension to the Document Object Model. Like other DOM objects, it is considered a host object and is not required to inherit from Object, nor its methods from Function, like native ECMAScript functions and objects do. This is the reason apply and call are undefined on those methods. In IE 9, most DOM objects were improved to inherit from native ECMAScript types. As the developer tools are considered an extension to IE (albeit, a built-in extension), they clearly didn't receive the same improvements as the rest of the DOM.
For what it's worth, you can still use some Function.prototype methods on console methods with a little bind() magic:
var log = Function.prototype.bind.call(console.log, console);
log.apply(console, ["this", "is", "a", "test"]);
//-> "thisisatest"
So you could fix up all the console methods for IE 9 in the same manner:
if (Function.prototype.bind && window.console && typeof console.log == "object"){
[
"log","info","warn","error","assert","dir","clear","profile","profileEnd"
].forEach(function (method) {
console[method] = this.bind(console[method], console);
}, Function.prototype.call);
}
This replaces the "host" functions with native functions that call the "host" functions. You can get it working in Internet Explorer 8 by including the compatibility implementations for Function.prototype.bind and Array.prototype.forEach in your code, or rewriting the above snippet to incorporate the techniques used by those methods.
See also
console.log typeof is "object" instead of "function" - Microsoft Connect (Live account required)
There is also Paul Irish's way of doing it. It is simpler than some of the answers above, but makes log always output an array (even if only one argument was passed in):
// usage: log('inside coolFunc',this,arguments);
// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
log.history = log.history || []; // store logs to an array for reference
log.history.push(arguments);
if(this.console){
console.log( Array.prototype.slice.call(arguments) );
}
};
Several of IE's host object functions aren't really JavaScript functions and so don't have apply or call. (alert, for example.)
So you'll have to do it the hard way:
function debug()
var index;
if(!window.console) {
window.console = { log: function() { /* do something */ } };
}
for (index = 0; index < arguments.length; ++index) {
console.log(arguments[index]);
}
}
I came across the same IE trouble and made a routine for it.
It is not as fancy as all the above implementations, but it works in ALL modern browsers.
I tested it with Firefox (Firebug), IE 7,8,9 Chrome and Opera.
It makes use of the evil EVAL, but you will only want to debug in development.
Afterwards you will replace the code with debug = function () {};
So here it is.
Regards, Hans
(function(ns) {
var msgs = [];
// IE compatiblity
function argtoarr (args,from) {
var a = [];
for (var i = from || 0; i<args.length; i++) a.push(args[i]);
return a;
}
function log(arg) {
var params = "", format = "", type , output,
types = {
"number" : "%d",
"object" : "{%o}",
"array" : "[%o]"
};
for (var i=0; i<arg.length; i++) {
params += (params ? "," : "")+"arg["+i+"]";
type = types[toType(arg[i])] || "%s";
if (type === "%d" && parseFloat(arg[i]) == parseInt(arg[i], 10)) type = "%f";
format += (format ? "," : "")+type;
}
// opera does not support string format, so leave it out
output = "console.log("+(window.opera ? "" : "'%f',".replace("%f",format))+"%p);".replace("%p",params);
eval(output);
}
ns.debug = function () {
msgs.push(argtoarr(arguments));
if (console !== undefined) while (msgs.length>0) log(msgs.shift());
}
})(window);
Oops forgot my toType function, here it is.
function toType(obj) {
if (obj === undefined) return "undefined";
if (obj === null) return "null";
var m = obj.constructor;
if (!m) return "window";
m = m.toString().match(/(?:function|\[object)\s*([a-z|A-Z|0-9|_|#]*)/);
return m[1].toLowerCase();
}
Ok, it works when you write it this way:
function debug()
if(!window.console) {
window.console = {};
console.log = function() { /* do something */ };
}
console.log.apply(console, arguments);
}
Odd behaviour... but if you write it this way 'console.log' gets recognized as a function.
The reason I came to this question was that I as trying to 'spicy' the console.log function for a specific module, so I'd have more localized and insightful debug info by playing a bit with the arguments, IE 9 broke it.
#Andy E answer is great and helped me with lots of insight about apply. I just don't take the same approach to support IE9, so my solution is running the console only on "modern browsers" (being that modern means whatever browsers that behave the way I expect =)
var C = function() {
var args = Array.prototype.slice.call(arguments);
var console = window.console;
args[0] = "Module X: "+args[0];
if( typeof console == 'object' && console.log && console.log.apply ){
console.log.apply(console, args);
}
};
Try:
function log(type) {
if (typeof console !== 'undefined' && typeof console.log !== 'undefined' &&
console[type] && Function.prototype.bind) {
var log = Function.prototype.bind.call(console[type], console);
log.apply(console, Array.prototype.slice.call(arguments, 1));
}
}
log('info', 'test', 'pass');
log('error', 'test', 'fail');
Works for log, debug, info, warn, error, group or groupEnd.