Positioning On Across Different Browsers - javascript

When I create web page, I want all features on the page to be placed on the same position in different browsers. Sometimes some features are placed on different positions, when I run the page on different browsers.I am using these variables to determine what browser is being opened right know and with if statement for example I determine some options.
//var firefox = (navigator.userAgent.indexOf("Firefox") != -1);
//var opera = (navigator.userAgent.indexOf("Opera") != -1);
//var ie = (navigator.userAgent.indexOf("MSIE") != -1);
//var safari = (navigator.userAgent.indexOf("Safari") != -1);
//var scrome = (navigator.userAgent.indexOf("Chrome") != -1);
1. Is it a good way to do like this when some features are placed on different positions in different browsers ?
2.What type is returned when I am using 'navigator.userAgent.indexOf(...)'. Is it boolean or some kind of collection which returns the position of the parameter, like the hashtable does ?. What type returns exactly ?

Different browsers may have different behavior of initial positions, a good practice to make them behaves same is to do CSS reset.
navigator.userAgent is a string object, thus .indexOf(...) returns integer Number

(1) don't do browser detection based on the user agent string. This is highly unreliable and very much not 2013. You want to use a framework like modernizr to detect which features are supported (and which are not). This is a good start.
(2) Not a collection, just a number that describes the position of the searched string in the original string. See here, or find out yourself:
typeof(navigator.userAgent.indexOf("foo"))
(3) BTW: amazing name for posting beginner questions ;)

so you r looking for this i think :
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="style.css" />
<!-->
that code should be place in the Head section of the page you could specify a multiple css page for each browser this link is for IE(internet explorer) for example

Related

Javascript substring and indexOf not working in IE9

I have this Javascript here:
function getTxt(obj) {
var first = obj.innerHTML.substring(0, obj.innerHTML.indexOf('<span class=\"item2\">'));
var second = obj.innerHTML.substring(obj.innerHTML.indexOf('<span class=\"item2\">'));
var f = first.replace(/(<([^>]+)>)/ig,'');
var s = second.replace(/(<([^>]+)>)/ig,'');
alert(first + "\n" + second + "\n" + f + "\n" + s);
}
and the HTML:
<span class="item" onclick="getTxt(this)"><span class="item1">MyName</span><span class="item2">555-555-5555</span></span>
In most browsers (FireFox, Chrome, Safari, Opera) it will alert:
<span class="item1">MyName</span>
<span class="item2">555-555-5555</span>
MyName
555-555-5555
as expected. However, in IE9 it alerts:
<span class="item1">MyName</span><span class="item2">555-555-5555</span>
MyName555-555-5555
So it puts the vars "first" and "second" together into var "first", and puts "f" and "s" together into var "f".
I would like to know if there is anyway to correct this for IE9 (and probably other version of IE also) to work as it does in the other browsers.
Pattern matching innerHTML is particularly a problem in IE and is generally a bad idea. IE often does NOT return to you the same HTML that was originally in the page. It often requotes or removes quotes, changes the order of attributes, changes case, etc... IE is clearly reconstituting the HTML rather than give you back what was originally in the page. As such, you cannot reliably pattern match innerHTML in IE. There are some specific things you can probably match (the start of tags), but you can't expect attributes to be in a specific spot or to have a specific format.
If you console.log(obj.innerHTML) in IE, you will likely see what I'm talking about. It will look different.
A more robust solution is to use the DOM functions to navigate the specific elements or CSS selectors to find specific objects and then change attributes or innerHTML on a single specific element. Let the DOM navigation find the right element for you rather than parsing the HTML yourself.
If you provide a desired before and after sample of the HTML and describe what you're trying to accomplish, folks here can probably help you get the job done with DOM manipulation rather than HTML parsing.
I don't know which selector libraries you have available to you or which browsers you're targeting, but in jQuery, you could do this like this:
function getText(obj) {
return $(obj).find(".item1").text();
}
In plain javascript, in IE8 and above and all other modern browsers, you can use this:
function getText(obj) {
return obj.querySelectorAll(".item1").innerHTML;
}
If you had to support back to IE6 or IE7, I'd suggest getting the Sizzle library and use that for your queries:
function getText(obj) {
return Sizzle(".item1", obj)[0].innerHTML;
}
It happens because of "Quirky mode" in Internet Explorer. It's a huge pain in the ass, but you can disable it in IE DevTools, or by adding this metatag to your page:
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

Is there an acceptable cross-platform method for displaying a numeric keypad in standard web forms on a touch-based device?

The goal: To find a cross-platform solution for displaying numeric keyboards on mobile touch-based devices, with a minimum of hacks.
The problem:
I have a regular web application using data input forms, containing primarily numeric data. When a user interacts with my site on a mobile device, I would like to display a numeric virtual keypad as most standard keyboards require a second press to switch from alphas to numbers. I know that i can trigger different keyboard by setting the "type" attribute of input element:
type=number:
This works great under iOS/Safari. I am unsure about other browsers on the platform.
On Android, this does not consistently raise the correct keyboard on various browsers and often results in unwanted elevator buttons on the input. I have yet to find a clean way to turn these off in CSS.
type=tel:
This almost works on iOS/Safari, but the telephone keyboard lacks a decimal button.
Seems to work great across multiple android browsers, without any extraneous UI elements added to the page.
My current solution is hacky and simplistic. Based on a class that I'm already using for numeric validation, I replace each text element that should contain a number with a new input that is either a number or a tel type based on the detected OS/browser.
var isAndroid = navigator.userAgent.match(/android/i) ? true : false;
var isIOS = navigator.userAgent.match(/(ipod|ipad|iphone)/i) ? true : false;
if (isAndroid || isIOS) {
var useNumberType = (isIOS ? true : false); //iOS uses type=number, everyone else uses type=tel
jQuery("input.num").each(function () {
var type = (useNumberType ? "number" : "tel");
var html = this.outerHTML;
html = html.replace(/(type=\"?)text(\"?)/, "$1" + type + "$2");
this.outerHTML = html;
});
}
I would prefer to not use browser detection and to not change out the inputs on the fly at run time. I could possibly introduce an http module on the server side that did basically the same thing, but that is not substantially better. I'm shocked that there isn't a CSS call for this.
Is there a better way to get a numeric keyboard with a decimal button, that works on all or most touch-based mobile devices without adding weird UI elements to the page?
-------------- update
I don't think there is a way to do what I really want to do, which is to setup a single input style or type that will work well across desktop browsers and all major mobile touch-based platforms. I settled on changing the type of the input through a direct DOM call rather through jQuery instead of rewriting the entire input via outerHTML. I suspect there isn't much difference in effect, but the code is a little cleaner. Since I'm not changing input types on the desktop, I shouldn't have to worry about IE's read only restriction on the attribute.
Ideally, I'd probably handle this on the server side so everything got sent to the browser in the format desired for the device making the request. But for now the new code looks more like this:
var isAndroid = navigator.userAgent.match(/android/i) || navigator.platform.match(/android/i) ? true : false;
var isIOS = navigator.userAgent.match(/(ipod|ipad|iphone)/i) ? true : false;
if (isAndroid || isIOS) {
var useNumberType = (isIOS ? true : false); //iOS uses type=number, everyone else uses type=tel
jQuery("input.num").each(function () {
var type = (useNumberType ? "number" : "tel");
if (this.type == "text") {
this.type = type;
}
});
}
Protip: when working with mobile, do NOT interfere with the user experience. This means keeping the built-in keypads as they are.
Some users may even have Javascript disabled on their mobile devices/browsers!
What you should do here is include an HTML hint to the browser. This way, mobile browsers should know what kind of content they are interacting with.
HTML5 includes several new <input> content types that should be supported on all modern mobile devices (and most modern browsers)
You can find the full list here.
What you want specifically is the following:
Old code:
Phone number: <input type="text" name="phone" />
New code:
Phone number: <input type="tel" name="phone" />
I don't know that any browsers currently support "tel", so you could use something like the following:
Phone number: <input type="number" name="phone" min="1000000000" max="9999999999" />
This is a bit of a hack, but is another option for you.
This is a MUCH simpler and more maintainable way of doing things, and better for the user.
Please let me know if you have any questions. I know this isn't directly answering the question, but it is a better way of doing things for now, in my opinion. :)
EDIT:
A possible way to get around this for each browser is by checking the user agent using JS/Jquery. I'm not sure exactly how to do this, but here is a tutorial on how to do so in .NET and changing the CSS for each element using JQuery.
EDIT EDIT!:
Try just modifying your code as such:
var isAndroid = navigator.userAgent.match(/android/i) ? true : false;
var isIOS = navigator.userAgent.match(/(ipod|ipad|iphone)/i) ? true : false;
if(isIOS)
$(.phoneInput).attr("type", "tel");
if(isAndroid)
{
$(.phoneInput).attr("type", "number");
$(.phoneInput).attr("min", "1000000000");
$(.phoneInput).attr("max", "9999999999");
}
I hope this out of everything works! You might have to switch the two if statements, depending on how your testing turns out.

XPath queries in IE use zero-based indexes but the W3C spec is one-based. How should I handle the difference?

The Problem
I am converting a relatively large piece of Javascript that currently only works on Internet Explorer in order to make it work on the other browsers as well. Since the code uses XPath extensively we made a little compatibility function to make things easier
function selectNodes(xmlDoc, xpath){
if('selectNodes' in xmlDoc){
//use IE logic
}else{
//use W3C's document.evaluate
}
}
This is mostly working fine but we just came across the limitation that positions in IE are zero-based but in the W3C model used by the other browsers they are one-based. This means that to get the first element we need to do //books[0] in IE and //books[1] in the other browsers.
My proposed solution
The first thought was using a regex to add one to all indexes that appear in the queries if we are using the document.evaluate version:
function addOne(n){ return 1 + parseInt(nStr, 10); }
xpath = xpath.replace(
/\[\s*(\d+)\s*\]/g,
function(_, nStr){ return '[' + addOne(nStr) + ']'; }
);
My question
Is this regex based solution reasonably safe?
Are there any places it will convert something it should not?
Are there any places where it will not convert something it should?
For example, it would fail to replace the index in //books[position()=1] but since IE doesn't appear to support position() and our code is not using that I think this particular case would not be a problem.
Considerations
I downloaded Sarissa to see if they have a way to solve this but after looking at the source code apparently they don't?
I want to add one to the W3C version instead of subtracting one in the IE version to ease my conversion effort.
In the end
We decided to rewrite the code to use proper XPath in IE too by setting the selection language
xmlDoc.setProperty("SelectionLanguage", "XPath");
we just came across the limitation that positions in IE are zero-based
but in the W3C model used by the other browsers they are one-based.
This means that to get the first element we need to do //books[0] in
IE and //books[1] in the other browsers.
Before doing any XPath selection, specify:
xmlDoc.setProperty("SelectionLanguage", "XPath");
MSXML3 uses a dialect of XSLT/XPath that was in use before XSLT and XPath became W3C Recommendations. The default is "XSLPattern" and this is what you see as behavior.
Read more on this topic here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms754679(v=vs.85).aspx
Why not modify the original expressions, so that this:
var expr = "books[1]";
...becomes:
var expr = "books[" + index(1) + "]";
...where index is defined as (pseudocode):
function index(i) {
return isIE ? (i - 1) : i;
}

Writing Conditional Code for Internet Explorer WITHIN a Javascript file

I'd like to know if it's possible to write conditional javascript within a javascript file for Internet Explorer.
i.e. something like this...
if (is IE7) {
do this } else {
do this instead
});
I know I can load a completely different script for IE using conditional comments in the head, but I only want to change a small piece of code and so loading a completely different sheet would be an 'expensive' way to do that.
When writing Javascript, doing feature detection is always the way to go instead of browser detection. So instead of doing if (IE7) do if (feature).
For example, if you want to know if your browser supports getElementsByClassName(), instead of checking the browser version, you check for the existence of the function ( if (document.getElementsByClassName) ).
Please read this great article:
Object detection on Quirksmode
If you want to know whether the
browser that views your page supports
certain objects you want to use in
your code, you should never EVER use a
browser detect. Sure, you know that
this–and–that browser will support
your code while such–and–so browser
won’t. But how about other browsers,
obscure browsers?
Not within the JavaScript file directly.
A few alternatives would be:
Using a global variable before the script is loaded to check in your JavaScript file. This is a bit of a hybrid approach and could get messy, but it's guaranteed IE detection.
<!--[if IE]>
<script type="text/javascript">
var is_ie = true;
</script>
<![endif]-->
<script type="text/javascript" src="somefile.js"></script>
Or, a more traditional approach using browser or object detection within the JavaScript file.
Conditional compilation is exactly what you are looking for.
<script>
/*#cc_on
#if (#_jscript_version == 5.7 && window.XMLHttpRequest)
document.write("You are using IE7");
#end
#*/
</script>
My go-to script for this is PPK's BrowserDetect script. It's lightweight, easily understandable, and doesn't require you to use a library. When it's loaded, you can write code like:
if (BrowserDetect.browser == "Explorer" && BrowserDetect.version >= 6 && BrowserDetect.version <= 8) {
// IE6-8 code
{
Of course, you should avoid using this at all (reasonable) costs, but there's times where it's cleaner to quarantine IE-specific code away rather than try to hack around IE-specific functions and bugs.
Despite the fact that this is an answer to the original question, this is NOT what you should do. So don't do it!
Why not work out which browser you are using and store that in a variable in javascript. Then you can have if statemenets and the like in your javascript. e.g. If I am IE then do this, otherwise do that. You get the idea!
Have you seen this? Browser sniffing
The salient bit:
var is = {
ff: window.globalStorage,
ie: document.all && !window.opera,
ie6: !window.XMLHttpRequest,
ie7: document.all && window.XMLHttpRequest && !XDomainRequest && !window.opera,
ie8: document.documentMode==8,
opera: Boolean(window.opera),
chrome: Boolean(window.chrome),
safari: window.getComputedStyle && !window.globalStorage && !window.opera
}
If you are using jquery you code do this
if ($.browser.msie && $.browser.version == '6.0') {
//do IE specific code
}
If you want to use jquery, it has a built in browser detect.
http://api.jquery.com/jQuery.browser/

Dynamic CSS3 prefix user agent detection

Is there a better way then using jQuery.browser, or equivalents, for determining css 3 prefixes (-moz, -webkit, etc), as it is disencouraged? Since the css is dynamic (the user can do anything with it on runtime), css hacks and style tag hacks can't be considered.
I don't see the issue with using the navigator.userAgent to determine if you need to cater for Webkit / Gecko CSS3 prefixes. Or better yet, just stick with CSS2 until CSS3 becomes a W3C Recommendation.
The reason use of the navigator object is discouraged is because it was used over Object detection when (java)scripting for different browsers, your situation is one where it is fine to use user agent detection, because your'e specifically targeting certain quirks with different rendering engines.
Edit:
Picking up from where cy left off, you can use javascript object detection to detect whether a prefix is used, I made some quick code to do so:
window.onload = function ()
{
CSS3 = {
supported: false,
prefix: ""
};
if (typeof(document.body.style.borderRadius) != 'undefined') {
CSS3.supported = true;
CSS3.prefix = "";
} else if (typeof(document.body.style.MozBorderRadius) != 'undefined') {
CSS3.supported = true;
CSS3.prefix = "-moz-";
} else if (typeof(document.body.style.webkitBorderRadius) != 'undefined') {
CSS3.supported = true;
CSS3.prefix = "-webkit-";
}
if (CSS3.supported)
if (CSS3.prefix == "")
alert("CSS3 is supported in this browser with no prefix required.");
else
alert("CSS3 is supported in this browser with the prefix: '"+CSS3.prefix+"'.");
else
alert("CSS3 is NOT supported in this browser.");
};
Remember to watch out for strange quirks such as -moz-opacity which is only supported in older versions of Firefox but has now been deprecated in favour of opacity, while it still uses the -moz- prefix for other new CSS3 styles.
Array.prototype.slice.call(
document.defaultView.getComputedStyle(document.body, "")
)
.join("")
.match(/(?:-(moz|webkit|ms|khtml)-)/);
Will return an array with two elements. One with dashes and one without dashes, both lowercase, for your convenience.
Array.prototype.slice.call(
document.defaultView.getComputedStyle(document.body, "")
);
Without the browser check will return an array of nearly all the css properties the browser understands. Since it's computed style it won't display shorthand versions, but otherwise I think it gets all of them. It's a quick hop skip and a jump to auto detect whatever you need as only vendor prefixed stuff starts with a dash.
IE9, Chrome, Safari, FF. Opera won't let you slice CSSStyleDeclaration for you can still use the same getComputedStyle code and loop through the properties or test for a specific one. Opera also wanted to be the odd man out and not report the vendor prefix dasherized. Thanks Opera.
Object.keys(CSSStyleDeclaration.prototype)
Works in IE9 and FF and reports the TitleCased (JavaScript) version of the vendor property names. Doesn't work in WebKit as the prototype only reports the methods.
Here's an interesting and very dangerous function I just wrote along these lines:
(function(vp,np){
Object.keys(this).filter(function(p){return vp=vp||p.match(/^(Moz|ms)/)}).forEach(function(op){
this.__defineGetter__(np=op.replace(vp[0], ""), function() { return this[op] });
this.__defineSetter__(np, function(val) { this[op] = val.toString() });
}, this);
}).call(CSSStyleDeclaration.prototype);
I didn't test anything Konquerer.
It's adding in another library, but would Modernizr work for you? It adds CSS classes to the <html> tag that can tell you what the browser supports.
It does muddy up the code a bit, but can certainly be helpful in appropriate situations.
Speculatively: Yes. You can try adding a vendor prefix css rule (that's what they're called), and then test to see if that rule exists. Those vendor-specific rules won't be added to the DOM in browsers in which they're not supported in some cases.
For example, if you try adding a -moz rule in webkit, it won't add to the DOM, and thus jQuery won't be able to detect it.
so,
$('#blah').css('-moz-border-radius','1px');
$('#blah').css('-moz-border-radius') //null in Chrome
Conversely,
$('#blah').css('-webkit-border-radius','1px');
$('#blah').css('-webkit-border-radius'); //returns "" in Chrome
This method works in WebKit browsers; I'm testing to see if it works in others. Pending.
Edit: Sadly, this isn't working in Firefox or Opera, which just returns "" no matter compatibility. Thinking of ways to do this cross-browser...
Final Edit: Andrew Dunn's answer does this in a way that works (at least in FF and Webkit, which is better than my method).
I use ternary operator to have it only in 1 line. If it's not webkit nor gecko, I'll just use the standard property. If it has no support, who really cares then?
var prefix = ('webkitAnimation' in document.body.style) ? '-webkit-' : ('MozAnimation' in document.body.style? '-moz-' : '');
Basically I found Animation is one of the properties never changed. As soon as the browser starts supporting the Draft / Candidate Recommendation of a CSS3 property, it drops the prefix on JS side. So you will need to be careful and take in mind that, before copy-pasting.

Categories