This is a function used to detect VML support in browsers. It's part of an html component file for giving border-radius and drop-shadow functionality to older versions of IE. I would like this explained to me, the step-by-step logic of it:
function supportsVml() {
if (typeof supportsVml.supported == "undefined"){
var a = document.body.appendChild(document.createElement('div'));
a.innerHTML = '<v:shape id="vml_flag1" adj="1" />';
var b = a.firstChild;
b.style.behavior = "url(#default#VML)";
supportsVml.supported = b ? typeof b.adj == "object": true;
a.parentNode.removeChild(a);
}
return supportsVml.supported
}
Where I am confused:
What is supporstVml.supported? Is this a variable, I don't see it declared anywhere else int he file...
what is the url(#default#VML) behavior?
supportsVml.supported is reassigned a new value based on the conditional, but I have no idea what or why...
Thanks!
supportsVml.supported is a property of the function, which is used just as a caching for the result.
If it is undefined (before the first call), the detection algorithm is run. Afterwards just the cached value is used and the detection is omitted.
The actual detection algorithm tries to add a default VML element and checks, whether this is inserted correctly. If so, VML is supported.
EDIT
The behavior can attach scripts to the CSS of an element (link). As far as I know, this is unique to older IE versions.
supportsVml.supported = b ? typeof b.adj == "object": true;
This line uses a ternary operator. It could be rewritten as follows:
if ( b ) {
if ( b.adj == 'object' ) {
supportsVml.supported = true;
} else {
supportsVml.supported = false;
}
} else {
supportsVml.supported = true;
}
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 am writing this method encodedURIComponentValue() for Javascript string:
the idea is to allow me to call : "some string".encodedURIComponentValue()
The code is as:
if (typeof String.prototype.encodedURIComponentValue != 'function') {
String.prototype.encodedURIComponentValue = function (str) {
if (str && str.length > 0)
return encodeURIComponent(str);
else
return "";
};
}
but in some case it does not work:
var encodedVal = $("body").find("option:selected").first().text().encodedURIComponentValue() // text() = "Option1"
console.log(encodedVal); // I only see "" (empty)
Any idea ?
You may find the following answer helpful as it explains prototype, constructor function and the value of this.
In this case I would not advice doing it like you do. You don't own String and modifying it breaks encapsulation. The only "valid" situation would be if you need to implement an existing method to support older browsers (like Object.create). More info on that here.
You could do what you're doing with:
encodeURIComponent(
$("body").find("option:selected").first().text()
);
So other then liking the other syntax there really isn't any reason for it.
OK, it is my stupid mistake - the str is the parameter which was never supplied.
So I changed to this and it works:
if (typeof String.prototype.encodedURIComponentValue != 'function') {
String.prototype.encodedURIComponentValue = function () {
if (this && this.length > 0)
return encodeURIComponent(this);
else
return "";
};
}
Hope I will understand more about this keyword in Js
This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 8 years ago.
I have to check deeply-nested object property such as YAHOO.Foo.Bar.xyz.
The code I'm currently using is
if (YAHOO && YAHOO.Foo && YAHOO.Foo.Bar && YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
}
This works, but looks clumsy.
Is there any better way to check such deeply nested property?
If you expect YAHOO.Foo.Bar to be a valid object, but want to make your code bulletproof just in case it isn't, then it can be cleanest to just put a try catch around it and let one error handler catch any missing segment. Then, you can just use one if condition instead of four that will detect if the terminal property exists and a catch handler to catch things if the intermediate objects don't exist:
try {
if (YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// handle error here
}
or, depending upon how your code works, it might even just be this:
try {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// do whatever you want to do when YAHOO.Foo.Bar.xyz doesn't exist
}
I particularly use these when dealing with foreign input that is supposed to be of a particular format, but invalid input is a possibility that I want to catch and handle myself rather than just letting an exception propagate upwards.
In general, some javascript developers under-use try/catch. I find that I can sometimes replace 5-10 if statements checking input with a single try/catch around a larger function block and make the code a lot simpler and more readable at the same time. Obviously, when this is appropriate depends upon the particular code, but it's definitely worth considering.
FYI, if the usual operation is to not throw an exception with the try/catch, it can be a lot faster than a bunch of if statements too.
If you don't want to use the exception handler, you can create a function to test any arbitrary path for you:
function checkPath(base, path) {
var current = base;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
Example usage:
var a = {b: {c: {d: 5}}};
if (checkPath(a, "b.c.d")) {
// a.b.c.d exists and can be safely accessed
}
var _ = {};
var x = ((YAHOO.Foo || _).Bar || _).xyz;
Consider this utility function:
function defined(ref, strNames) {
var name;
var arrNames = strNames.split('.');
while (name = arrNames.shift()) {
if (!ref.hasOwnProperty(name)) return false;
ref = ref[name];
}
return true;
}
Usage:
if (defined(YAHOO, 'Foo.Bar.xyz')) {
// operate on YAHOO.Foo.Bar.xyz
}
Live demo: http://jsfiddle.net/DWefK/5/
If you need to check the correctness of the path, rather than the existance of the "xyz" member on the "YAHOO.Foo.Bar" object, it will probably be easiest to wrap the call in a try catch:
var xyz;
try {
xyz = YAHOO.Foo.Bar.xyz;
} catch (e) {
// fail;
};
Alternately, you can do some string-kong-fu-magicTM:
function checkExists (key, obj) {
obj = obj || window;
key = key.split(".");
if (typeof obj !== "object") {
return false;
}
while (key.length && (obj = obj[key.shift()]) && typeof obj == "object" && obj !== null) ;
return (!key.length && typeof obj !== "undefined");
}
The use as follows:
if (checkExists("YAHOO.Foo.Bar.xyz")) {
// Woo!
};
This problem is solved quite beautifully by coffeescript (which compiles down to javascript):
if YAHOO.Foo?.Bar?.xyz
// operate on YAHOO.Foo.Bar.xyz
use a try catch.
a={
b:{}
};
//a.b.c.d?true:false; Errors and stops the program.
try{
a.b.c.d;
}
catch(e){
console.log(e);//Log the error
console.log(a.b);//This will run
}
I actually voted to close the question as duplicate of javascript convert dotnotation string into objects.
However, I guess it's a different topic, but the answer there might still be helpful if you don't want to try-catch all the time.
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.
I want to change a flash object enclosed within with jQuery after an onClick event. The code I wrote, essentially:
$(enclosing div).html('');
$(enclosing div).html(<object>My New Object</object>);
works in Firefox but not in IE. I would appreciate pointers or suggestions on doing this. Thanks.
The empty() method is the better way of deleting content. Don't know if that will solve your problem though :)
$('#mydiv').empty();
You could also try the replaceWith(content) method.
This is what I'm doing - it's taken from the swfobject and modified slightly:
function removeObjectInIE(el) {
var jbo = (typeof(el) == "string" ? getElementById(el) : el);
if (jbo) {
for (var i in jbo) {
if (typeof jbo[i] == "function") {
jbo[i] = null;
}
}
jbo.parentNode.removeChild(jbo);
}
}
function removeSWF(id) {
var obj = (typeof(id) == "string" ? getElementById(id) : id);
if(obj){
if (obj.nodeName == "OBJECT" || obj.nodeName == "EMBED") {
if (ua.ie && ua.win) {
if (obj.readyState == 4) {
removeObjectInIE(id);
}
else {
$(document).ready(function() { removeObjectInIE(id); });
}
}
else {
obj.parentNode.removeChild(obj);
}
}else if(obj.childNodes && obj.childNodes.length > 0){
for(var i=0;i<obj.childNodes.length;i++){
removeSWF(obj.childNodes[i]);
}
}
}
}
So you would then do removeSWF("mydiv"); - You'd probably want to rewrite this in a jQuery manner, this is taken from a library I am working on where I can't be certain jQuery will be there. (I replaced my on ready function with the $().ready())
Try the $().remove() method. This also removes event handlers to avoid memory leak problems and is one of the reasons why just setting the HTML to empty is not a good idea.