Event Delegation in iOS Safari - Event Bubbling Safari Quirk

April 15th, 2014

A recent quirk I found with coding for iOS Safari Mobile using event delegation and bubbling in Javascript:

In some cases you may want to perform some event bubbling so that when triggering an event on a DOM element, that event bubbles up to a parent element where you can capture and decide how to react on that event.  This is useful if you are capturing events on DOM elements NOT yet present in the DOM on page load, but later inserted into the DOM by some JS you are running.

In my instance, I was running a search autocomplete pulling results from an external API with a given user query term.  The results from the API are dynamically inserted into the DOM as the user types so the user can select from a list of near result matches.

Obviously you have to bind an event to the result set when the user clicks, but you must do this using event bubbling on an existing parent element since the element in question won't be created until after the page loads.  With JQuery, you simply do this by binding on an existing parent element and adding the child element (yet created) to the bind like this:

$('body').on('click', '.search-result-row', function() {
     /* ... your code here .. */

When any element with class "search-result-row" is clicked, the event bubbles up to the body where it has been binded to this function and body is a universal enough parent element to bind to.

Now here's the quirk:  The above code works FINE on desktop browsers but on iOS Safari Mobile the event doesn't bubble all the way to the body!  So if you were attempting to capture events bubbling up to your body element, this would be rendered useless in Safari.   There seems to be some ways around this using CSS.   Ultimately, this is definitely something to watch out for if you are expecting users using iOS Safari to view your site.

A quick obvious solution is to capture events on another parent element instead of the body element: 

$('.search-result-wrapper').on('click', '.search-result-row', function() {
      /* ... your code here .. */ 

Hope that helps!