I found two solutions for the time being to this problem, both of which obviously using preventDefault
on the external links.
If you're linking to another website or something to download, the only option I see is to ironically alert the user to hold their finger on the link to get the touch callout prompt. Then again, depending if it's a website or a PDF, instruct them to either copy the link or in the case of a PDF, add it to their reading list. Since the alert and confirm modals are also broken you'll need to implement your own modal notifications. If you already have that it shouldn't be that much trouble.
Update [2013-10-25] Apparently it's been fixed in iOS 7.0.3 and links open in Safari...
Edit [2013-10-05] Here's pretty much what I use with a jQuery UI modal
// iOS 7 external link polyfill
$('a[rel=external], a[rel=blank], a[target=_blank], a[href$=".pdf"]').on('click', function(e) {
if (navigator.standalone && /iP(hone|od|ad) OS 7/.test(navigator.userAgent)) {
e.preventDefault(); e.stopPropagation();
var href = $(this).attr('href');
var $dialog = $('<div id="ios-copy"></div>')
.html('<p>iOS 7 prevents us from opening external links in Safari, you can continue to the address and risk losing all navigation or you can copy the address to your clipboard by <strong>holding your finger on the link</strong> for a few seconds.</p><p><a style="background-color: rgba(0,0,0,.75); color: #fff; font-size: 1.25em; padding: 1em;" href="' + href + '">' + href + '</a></p>')
.appendTo('body')
.dialog({
title: 'External link',
modal: true,
buttons: {
Ok: function() {
$( this ).dialog( "close" );
}
}
});
}
});
The other workaround is using ajax or an iframe to load the external content, but unless you have a good sub-browser or something in your app it will look sketchy. Here's something along those lines.
// iOS 7 external link polyfill
if (/iP(hone|od|ad) OS 7/.test(navigator.userAgent) && window.navigator.standalone) {
$('a[rel=external], a[href$=".pdf"]').on('click', function(e) {
e.preventDefault(); e.stopPropagation();
var link = this;
var href = $(link).attr('href');
var frameContainer = $('<div></div>').css({
position: 'absolute',
left: 10,
top: $(link).position().top,
opacity: 0,
overflow: 'scroll',
'-webkit-overflow-scrolling': 'touch',
height: 520,
transition: 'opacity .25s',
width: 300
});
var iosFrame = $('<iframe class="iosFrame" seamless="seamless" width="1024" height="5000"></iframe>')
.attr('src', href)
.css({
height: 5000,
'max-width': 1024,
width: 1024,
overflow: 'scroll !important',
'-webkit-overflow-scrolling': 'touch !important'
});
var iosFrameClose = $('<a href="#"><i class="icon-cancel icon-remove icon-3x"></i></a>').css({
position: 'absolute',
left: -10,
top: $(link).position().top - 20,
'text-shadow': '1px 1px 1px #000',
transition: 'opacity .25s',
opacity: 0,
'-webkit-transform': 'translate3d(0, 0, 0)',
width: '3em',
height: '3em'
}).on('click', function(e) {
e.preventDefault();
setTimeout( function() {
$(frameContainer).remove();
$(iosFrameClose).remove();
}, 250);
});
iosFrame.appendTo(frameContainer);
frameContainer.appendTo('body');
iosFrameClose.appendTo('body');
iosFrame.contents().css({
'-webkit-transform': 'translate3d(0, 0, 0)'
});
// Show this thing
setTimeout( function() {
$(frameContainer).css({ opacity: 1 });
$(iosFrameClose).css({ opacity: 1 });
}, 1);
});
}