Tag Archives: Javascript

Deep linking into an iframe, cross-domain

Why would you want to do this? My use case was a gallery application where we needed to deep-link to a specific gallery entry. Alas, the gallery would be iframed. Yes, iframes should be avoided, but sometimes in real-life you have to just deal with it.

Imagine my surprise when I discovered that passing the parent window.location object into an iframe across domains is not only possible, but is easy and works in most browsers, down to IE8. Continue reading

IE innerHTML CSS bug

Today I encountered a bug that affects every version of Internet Explorer from IE6 all the way through IE9. Here’s what happens.

The Bug

  1. Set a Javascript string variable containing CSS styles (<style> or <link>) followed by some HTML content, such as a <p> tag (in my application this happened via JSONP).
  2. Add a <div> node to the DOM
  3. Insert the content from #1 into the .innerHTML property of the <div>
  4. The content gets inserted, devoid of CSS styles.
<!-- these styles are ignored when inserted via innerHTML -->
<style>
  p { color: blue; }
</style>
<p>Hello, how are you!</p>

The Solution

The simple solution was to put the other markup before the <style> or <link> tag. Bizarre.

<p>Hello, how are you!</p>
<!-- these styles are applied when inserted via innerHTML -->
<style>
 p { color: blue; }
</style>

Because IE needs something that affects the layout before it will apply CSS styles in an innerHTML property, these will not work as style triggers:

  • <!-- a comment -->
  • <p></p> (or any other empty block-level HTML tag)

Detecting mobile browsers via Javascript

Sometimes you want to do some sort of special processing in Javascript on mobile browsers, or vice versa. Here’s a quickie Javascript way to do that:

function is_mobile() {
	var agents = ['android', 'webos', 'iphone', 'ipad', 'blackberry'];
	for(i in agents) {
		if(navigator.userAgent.match('/'+agents[i]+'/i')) {
			return true;
		}
	}
	return false;
}

This version is fast and easy to extend to detect additional browsers by changing the agents array. Here’s a much more thorough version, modified from detectmobilebrowsers.com:

function is_mobile() {
	return /android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4));
}

Since this is a single gigantic regex, I suspect that it would run more slowly than the previous version, and is also more challenging to modify.

Instead of browser user-agent sniffing, you might want to consider using browser feature detection instead to tailor your application to the capabilities of the browser, rather than relying on the userAgent string (which can be customized in most browsers).

Enjoy!

Catching the Javascript beforeunload event, the cross-browser way

Javascript’s window.onbeforeunload event is useful for catching users that try to browse away from your page without having completed a certain action. Modern web applications such as Gmail and WordPress have made good use of this event.

Being a non-standard event originally invented by Microsoft back in the IE4 days, window.onbeforeunload has some real quirks, although thankfully every major modern browser does support it.

jQuery Doesn’t Help

Prior to jQuery 4, you couldn’t even bind to $(window).bind('beforeunload') due to a bug that has been fixed.

However, this isn’t your average Javascript event. window.onbeforeunload pops up a native dialog box that provides very little opportunity for customization beyond the text that is displayed to the user. There is no known way to disable this native dialog box and prevent normal behavior.

Tapping into jQuery’s $(window).unload() event doesn’t allow you to prevent the page from being unloaded, and I couldn’t get $(window).bind('beforeunload') to work at all in Firefox 3.6.

The Right Way

The right way turned out to be quite easy using native Javascript code (thanks to the Mozilla Doc Center for the working solution).

For IE and FF < 4, set window.event.returnValue to the string you wish to display, and then return it for Safari (use null instead to allow normal behavior):

window.onbeforeunload = function (e) {
    var e = e || window.event;

    // For IE and Firefox prior to version 4
    if (e) {
        e.returnValue = 'Any string';
    }

    // For Safari
    return 'Any string';
};

Javascript reserved words trigger “Expected Identifier” error on IE

If you’re getting an unreasonable “Expected Identifier” Javascript error on IE6/7, check to see if you have any variable names which are reserved words.

This also goes for HTML form element names:

<form name="aform">
    <input type="text" name="name" />
</form>

Then accessing document.aform.name.value would throw an error since name is a reserved word.

Javascript Reserved Words

  • abstract
  • alert
  • Anchor
  • Area
  • arguments
  • Array
  • assign
  • blur
  • boolean or Boolean
  • break
  • Button
  • byte
  • callee
  • caller
  • captureEvents
  • case
  • catch
  • char
  • Checkbox
  • class
  • clearInterval
  • clearTimeout
  • close
  • closed
  • comment
  • confirm
  • const
  • constructor
  • continue
  • Date
  • debugger
  • default
  • defaultStatus
  • delete
  • do
  • document
  • Document
  • double
  • Element
  • else
  • enum
  • escape
  • eval
  • export
  • extends
  • false
  • FileUpload
  • final
  • finally
  • find
  • float
  • focus
  • for
  • Form
  • Frame
  • frames
  • function
  • Function
  • getClass
  • goto
  • Hidden
  • history or History
  • home
  • if
  • Image
  • implements
  • import
  • in
  • Infinity
  • innerHeight
  • innerWidth
  • instanceOf
  • int
  • interface
  • isFinite
  • isNan
  • java
  • JavaArray
  • JavaClass
  • JavaObject
  • JavaPackage
  • label
  • length
  • Link
  • location or Location
  • locationbar
  • long
  • Math
  • menubar
  • MimeType
  • moveBy
  • moveTo
  • name
  • NaN
  • native
  • navigate
  • navigator or Navigator
  • netscape
  • new
  • null
  • Number
  • Object
  • onBlur
  • onError
  • onFocus
  • onLoad
  • onUnload
  • open
  • opener
  • Option
  • outerHeight
  • outerWidth
  • package
  • Packages
  • pageXoffset
  • pageYoffset
  • parent
  • parseFloat
  • parseInt
  • Password
  • personalbar
  • Plugin
  • print
  • private
  • prompt
  • protected
  • prototype
  • public
  • Radio
  • ref
  • RegExp
  • releaseEvents
  • Reset
  • resizeBy
  • resizeTo
  • return
  • routeEvent
  • scroll
  • scrollbars
  • scrollBy
  • scrollTo
  • Select
  • self
  • setInterval
  • setTimeout
  • short
  • static
  • status
  • statusbar
  • stop
  • String
  • Submit
  • sun
  • super
  • switch
  • synchronized
  • taint
  • Text
  • Textarea
  • this
  • throw
  • throws
  • toolbar
  • top
  • toString
  • transient
  • true
  • try
  • typeof
  • unescape
  • untaint
  • unwatch
  • valueOf
  • var
  • void
  • watch
  • while
  • window
  • Window
  • with

jQuery hoverIntent plugin

hoverIntent is a plug-in that works like (and was derived from) jQuery’s built-in hover. However, instead of immediately calling the onMouseOver function, it waits until the user’s mouse slows down enough before making the call. Why? To delay or prevent the accidental firing of animations or ajax calls. Simple timeouts work for small areas, but if your target area is large it may execute regardless of intent.

Logical XOR in Javascript

XOR (exclusive OR) is a boolean operation, like && and ||, but with the following logic: It is true if the expression on either side is true (like ||), but not if both sides are true (like &&).

Sometimes, in your JavaScript, you might want to do the following:

if( foo XOR bar ) { ... }

Unfortunately, JavaScript does not have a logical XOR operator. This article covers several methods of performing an XOR manually, and concludes with a surprisingly succinct way of doing it, summarized below.

The cleanest and best

The XOR operation can be described as “return true if the two boolean operands do not have the same value”. So the actual response that is wanted is just this:

if( foo != bar ) { ... }

That works if the two operands are both boolean true/false. However, to allow for other variable types (strings, expressions, etc.), it cannot work, since ‘string’ does not equal /regex/, even though the XOR operation would be expected to return false. So the operands need to be converted into boolean values first. This can be done like this:

foo ? true : false

However, it is easier and quicker to simply use the ! operator. This will give the opposite value – false instead of true, but as long as both operands get the same treatment, this will not affect the end result. So the simplest, cleanest version of the XOR operator, that works with any data types, including expressions, is this:

if( !foo != !bar ) { ... }