Is it possible to change the JavaScript alert title in a UIWebview
in iPhone?

- 7,598
- 6
- 38
- 61

- 801
- 4
- 11
- 24
-
4i want to change javascript alert dialog title of the web page in ios.what is bij? – VK.Dev May 21 '12 at 08:07
-
1This is an excellent question, and it's not easy to find information on this topic. If you downvoted, could you provide some info as to how the question could be improved? – Robert Karl May 13 '13 at 18:56
7 Answers
Basically you cannot change the title, but recently I have found a way to show an alert dialog with no title.
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", 'data:text/plain,');
document.documentElement.appendChild(iframe);
window.frames[0].window.alert('hello');
iframe.parentNode.removeChild(iframe);

- 873
- 1
- 9
- 8
-
2
-
It works fine in iOS 7, but in iOS8 it displays the text in bold font. Any idea? – Ramesh Sangili Oct 06 '14 at 20:34
-
It throws "uncaught exception: out of memory" in mozilla(version 41.0.2). Any idea? – Nits Nov 03 '15 at 15:05
-
Sometimes this changes existed divs' order in ios webview when "removeChild(iframe)". – Alan Wang Aug 13 '16 at 03:59
-
For those wondering, MacOS Safari doesn't have titles to the alerts. This doesn't affect MacOS Chrome's alerts either. – IcyIcicle Jan 07 '22 at 21:32
I present three separate solutions to this problem. The best solution for production code is the one described by @Martin H. My only addition to this is a concrete example showing how to implement it.
My other solutions depend on undocumented behaviors of UIWebView, but don't require any changes to the content/JS.
Solution 1: Here is a concrete example demonstrating the technique put forth by @Martin H. This is likely the best solution as it does not rely on UIWebView
/ UIAlertView
undocumented behaviors.
@interface TSViewController () <UIWebViewDelegate>
@end
@implementation TSViewController
- (UIWebView*) webView
{
return (UIWebView*) self.view;
}
- (void) loadView
{
UIWebView* wv = [[UIWebView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)];
wv.delegate = self;
wv.scalesPageToFit = YES;
self.view = wv;
}
- (void) viewDidLoad
{
[super viewDidLoad];
[self.webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.craigslist.org"]]];
}
- (void) webViewDidFinishLoad: (UIWebView *) webView
{
// inject some js to re-map window.alert()
// ideally just do this in the html/js itself if you have control of that.
NSString* js = @"window.alert = function(message) { window.location = \"http://action/alert?message=\" + message; }";
[webView stringByEvaluatingJavaScriptFromString: js];
// trigger an alert. for demonstration only:
[webView stringByEvaluatingJavaScriptFromString: @"alert('hello, world');" ];
}
- (BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL* url = request.URL;
// look for our custom action to come through:
if ( [url.host isEqualToString: @"action"] && [url.path isEqualToString: @"/alert"] )
{
// parse out the message
NSString* message = [[[[url query] componentsSeparatedByString: @"="] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// show our alert
UIAlertView* av = [[UIAlertView alloc] initWithTitle: @"My Custom Title"
message: message
delegate: nil
cancelButtonTitle: @"OK"
otherButtonTitles: nil];
[av show];
return NO;
}
return YES;
}
Solution 2: First, let me say that I would never personally use the following solution in production code. The best way to achieve this functionality is to do what @Martin H suggests in his answer, or possibly what @twk suggests if an empty title is actually what is desired. If you don't have control over the web content itself, perhaps you could inject @Martin H's code after the content has been loaded.
That said, this is otherwise achievable if we make some assumptions. First, that the Javascript alert()
method indeed maps to a real UIAlertView
. (In fact it does!) Second, that we can come up with some mechanism to discern an alert()
-sourced UIAlertView
from other application-sourced UIAlertViews
. (We can!)
My approach is to swizzle the UIAlertView
. I know - swizzling sucks and has all sorts of drawbacks. I don't swizzle in production apps if I can help it. But for this science project, we're going to swizzle the UIAlertView setDelegate:
method.
What I found is that 1) the UIWebView
will construct an UIAlertView
to display the javascript alert, and 2) the UIAlertView
title is set before the UIAlertView delegate
is set. By swizzling UIAlertView setDelegate:
with my own method I can accomplish two things: 1) I can determine that the UIAlertView
is being commissioned on behalf of a UIWebView
(just inspect the delegate...) and 2) I have opportunity to re-set the title before the alertview is shown.
You might ask "Why not just swizzle UIAlertview -show
method?" That would be great, but in my experimenting I found that the UIWebView never invoked show
. It must be calling some other internal method, and I didn't investigate further.
My solution implements a category on UIAlertView, which adds a couple of class methods. Basically you register a UIWebView with these methods, and provide a replacement title to be used. Alternatively you can provide a callback block that returns a title when invoked.
I used the iOS6 NSMapTable
class to keep a set of UIWebView-to-Title mappings, where the UIWebView is a weak key. This way I don't ever have to unregister my UIWebView
and everything gets cleaned up nicely. Thus, this current implementation is iOS6-only.
Here's the code, shown in-use in a basic view controller:
#import <objc/runtime.h>
typedef NSString*(^TSAlertTitleBlock)(UIWebView* webView, NSString* defaultTitle );
@interface UIAlertView (TS)
@end
@implementation UIAlertView (TS)
NSMapTable* g_webViewMapTable;
+ (void) registerWebView: (UIWebView*) webView alertTitleStringOrBlock: (id) stringOrBlock
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g_webViewMapTable = [NSMapTable weakToStrongObjectsMapTable];
// $wizzle
Method originalMethod = class_getInstanceMethod(self, @selector(setDelegate:));
Method overrideMethod = class_getInstanceMethod(self, @selector(setDelegate_ts:));
method_exchangeImplementations(originalMethod, overrideMethod);
});
[g_webViewMapTable setObject: [stringOrBlock copy] forKey: webView];
}
+ (void) registerWebView: (UIWebView*) webView alertTitle: (NSString*) title
{
[self registerWebView: webView alertTitleStringOrBlock: title];
}
+ (void) registerWebView: (UIWebView*) webView alertTitleBlock: (TSAlertTitleBlock) alertTitleBlock
{
[self registerWebView: webView alertTitleStringOrBlock: alertTitleBlock];
}
- (void) setDelegate_ts: (id) delegate
{
// call the original implementation
[self setDelegate_ts: delegate];
// oooh - is a UIWebView asking for this UIAlertView????
if ( [delegate isKindOfClass: [UIWebView class]] )
{
// see if we've registered a title/title-block
for ( UIWebView* wv in g_webViewMapTable.keyEnumerator )
{
if ( wv != delegate)
continue;
id stringOrBlock = [g_webViewMapTable objectForKey: wv];
if ( [stringOrBlock isKindOfClass: NSClassFromString( @"NSBlock" )] )
{
stringOrBlock = ((TSAlertTitleBlock)stringOrBlock)( wv, self.title );
}
NSParameterAssert( [stringOrBlock isKindOfClass: [NSString class]] );
[self setTitle: stringOrBlock];
break;
}
}
}
@end
@interface TSViewController () <UIWebViewDelegate>
@end
@implementation TSViewController
- (UIWebView*) webView
{
return (UIWebView*) self.view;
}
- (void) loadView
{
UIWebView* wv = [[UIWebView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)];
wv.delegate = self;
wv.scalesPageToFit = YES;
self.view = wv;
}
- (void) viewDidLoad
{
[super viewDidLoad];
// method 1: bind a title to a webview:
[UIAlertView registerWebView: self.webView alertTitle: nil];
/*
// method 2: return a title each time it's needed:
[UIAlertView registerWebView: self.webView
alertTitleBlock: ^NSString *(UIWebView *webView, NSString* defaultTitle) {
return @"Custom Title";
}];
*/
[self.webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.craigslist.org"]]];
}
- (void) webViewDidFinishLoad: (UIWebView *) webView
{
// trigger an alert
[webView stringByEvaluatingJavaScriptFromString: @"alert('hello, world');" ];
}
@end
Solution 3: Upon reflection, here's a simpler technique than I initially described in Solution 2. It still depends on the undocumented fact that the javascript alert is implemented using a UIAlertView that has its delegate set to the sourcing UIWebView. For this solution, simply subclass UIWebView and implement your own delegate method for UIAlertView willPresentAlertView:, and when it is called, reset the title to whatever you wish.
@interface TSWebView : UIWebView
@end
@implementation TSWebView
- (void) willPresentAlertView:(UIAlertView *)alertView
{
if ( [self.superclass instancesRespondToSelector: @selector( willPresentAlertView:) ])
{
[super performSelector: @selector( willPresentAlertView:) withObject: alertView];
}
alertView.title = @"My Custom Title";
}
@end
@interface TSViewController () <UIWebViewDelegate>
@end
@implementation TSViewController
- (UIWebView*) webView
{
return (UIWebView*) self.view;
}
- (void) loadView
{
UIWebView* wv = [[TSWebView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)];
wv.delegate = self;
wv.scalesPageToFit = YES;
self.view = wv;
}
- (void) viewDidLoad
{
[super viewDidLoad];
[self.webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.craigslist.org"]]];
}
- (void) webViewDidFinishLoad: (UIWebView *) webView
{
// trigger an alert
[webView stringByEvaluatingJavaScriptFromString: @"alert('hello, world');" ];
}
@end
-
1From your answer, I understand you're quite proficient in Objective-C. I can't really tell if you ever do any work with web views, but if you do, don't you think it would be more useful if you'd write a succinct example of the technique that Cordova/Phonegap uses to make a bridge from JS to Objective-C? I'm positive that one of the techniques they use is `window.location` in Javascript, and watching for changes to webview location in Objective-C, reading out parameters embedded in the URL. Then canceling navigation perhaps. I know the technique, but I can't write meaningful Objective-C. – Myrne Stol May 29 '13 at 21:53
-
1The reason I say is that now you have an answer for which you yourself say you wouldn't use it. The Phonegap technique is definitely widely deployed, so it's probably a reasonable solution. – Myrne Stol May 29 '13 at 21:54
-
@MerynStol - ok you got it. I added a succinct example for the window.location technique. Also a simpler variant on my original solution. – TomSwift May 30 '13 at 15:54
-
very informative! You should get the bounty. The subclassing of UIWebView looks appealing too. – Myrne Stol May 30 '13 at 16:05
-
@TomSwift solution 1 works for alert() function, but not work for location permission alert !!!. Solution 2 is not recommended. Solution 3 does not work, I can get the title, but changing alertView 's title seems to have no effect – onmyway133 Feb 19 '14 at 08:35
-
I found this answer only now, i have to say that this is the best explanation on how to handle js alerts till now. Thanks very much! – Giorgio Antonioli Feb 19 '15 at 01:06
No you can't do this in a Javascript alert.
But if the Javascript is yours then instead of calling the alert you could instead call a function that calls Objective C and invoke an iOS native alert.
If the Javascript isn't yours then using UIWebView you can inject some Javascript to override the default behaviour and change it to call an iOS native alert i.e. something like this
window.alert = function(message) {
window.location = "myScheme://" + message"
};
Then look for myScheme and extract message in UIWebView's shouldStartLoadWithRequest
Here's the standard method for invoking Objective-C from Javascript

- 1
- 1

- 37,738
- 44
- 184
- 378
For what it's worth, Phonegap/Cordova offers a navigator.notification.alert function. It allows specifying a custom title.

- 11,222
- 4
- 40
- 48
Just make a class for your UIWebView and make willPresentAlertView (alertView: UIAlertView) function in it. After that set alertView.title = "your fav title"
and it is done.
import UIKit
class webviewClass: UIWebView {
func willPresentAlertView(alertView: UIAlertView) {
alertView.title = "my custum title"
}
}
After that when ever an alert or confirm and... shown, your custom title will shown.

- 44,709
- 21
- 151
- 275

- 31
- 5
Going off of what @twk wrote, I simply overwrite the alert function. Works great on ios7.
function alert(words){
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", 'data:text/plain,');
document.documentElement.appendChild(iframe);
window.frames[0].window.alert(words);
iframe.parentNode.removeChild(iframe);
}

- 365
- 3
- 17
Use cordova-plugin-dialogs. It allows to create native dialogs.
Below code will create native alert:
navigator.notification.alert(message, alertCallback, [title], [buttonName])

- 1,501
- 2
- 23
- 51

- 1