Stopping JavaScript Errors When Opening a LinkButton in a New Window
By Scott Mitchell
Introduction
A previous article of mine, Displaying Text in the Browser's Status Bar When Mousing Over a LinkButton, examined how to enhance the LinkButton control to display custom text in the browser's status bar when the link was moused over. Specifically, the article illustrated how to build a custom, compiled Web control that extended the
System.Web.UI.WebControls.LinkButton
class, providing extra properties
and overriding existing LinkButton methods in order to tack on extra functionality to the existing LinkButton control.
(The article also looked at how to add properties to the enhanced LinkButton control to display a client-side confirm
messagebox when clicked; this messagebox had OK and Cancel buttons and only continued with the postback if the user
clicked the OK button.)
I recently received an email inquiry in my Inbox from loyal 4Guys reader David P., who asked:
I'm writing to you because I think you may be able to help me. I enjoyed your article about overriding Linkbutton very much, but I have a further need that you may have a solution to. A LinkButton is rendered like(To see an example of David's problem, click here. The hyperlink's markup is simply<a href="javascript:__doPostBack(.....)" onClick="alert('pressed')">Click me</a>
.My problem is that if I SHIFT+CLICK the LinkButton [or right-click on it and opt to open in a new window], a new window opens [with the JavaScript,
javascript:__doPostBack(.....)
, in the Address bar, resulting in a client-script error and a confused user.] ... I want to avoid that. I've found a solution:
<a href='#' onClick="__doPostBack(....);">Click me</a>
My problem is that I don't know which attribute to modify so that the
href
value is moved to theonClick
event... It must be done in an inherited control or codebehind.
<a href="javascript:var x = 4;" target="_blank">click here</a>
. Note that clicking
the link opens a new window that displays the JavaScript in the address bar and returns an error. This mimics the behavior
of an ASP.NET LinkButton that is clicked to have opened in a new window.)
Good question, David! There is a way to "rearrange" the values of the rendered LinkButton's attributes, but it isn't
as simple or straightfoward as one might hope. In this article we'll add additional code to the skmLinkButton
control examined previously in Displaying Text in the Browser's Status Bar When Mousing Over a LinkButton so that
it emits an href="#"
and the proper script in the client-side onclick
event. Read on to learn more!
Examining How the LinkButton Class Renders Its Href
Attribute
In order to properly extend the built-in LinkButton Web control in order to meet our objectives, it's important that we have a solid understanding as to how the LinkButton is rendered. If you use a tool like Reflector you can peer into the source code of the LinkButton class. All of the interesting work takes place in the
AddAttributesToRender(HtmlTextWriter)
method. This method, which is common to all Web controls, is responsible
for adding the attributes to the rendered HTML markup (in the case of the LinkButton, the HTML element rendered is
<a>
). The HtmlTextWriter instance that's passed into this method is the object to which the
rendered element's attributes are written.
The code, in C#, for the LinkButton's AddAttributesToRender(HtmlTextWriter)
method is as follows:
|
The first thing the method does is check to make sure that the LinkButton is being rendered within a Web Form (i.e., a
<form runat="server">
). Following that, the base class's AddAttributesToRender(HtmlTextWriter)
method
is called. This base class method does a number of things, including adding formatting information. (For example, if you set
the LinkButton's ForeColor
property to Red, the base class's AddAttributesToRender(HtmlTextWriter)
method
will inject in the rendered HTML element the attribute: style="color:red;"
.)
Next, if the control is enabled, the Href
attribute is assigned the appropriate value. If the LinkButton's
CausesValidation
property is True and there are validation controls on the page, the script emited to the
Href
attribute includes a check to only postback if all validators on the page that are using client-side
script report that their data is valid. (That's what the internal Util.GetClientValidatedPostback(this)
method
does.) If there is no validation controls on the page or if CausesValidation
is False, the JavaScript assigned to the
Href
attribute is simply the call to the client-side __doPostBack(...)
function. (The
__doPostBack(...)
function is automatically added by the ASP.NET Page class when using a Web Form. This JavaScript
function causes the form to be posted back, just like had a standard Submit button been clicked.)
Moving the Href
Attribute Value to the Onclick
Attribute
As aforementioned, the problem with having the JavaScript call to
__doPostBack(...)
in the Href
attribute is that opening the link in a new window will produce undesireable effects. What we'd rather do, is have the
Href
element have a value of #
and have the client-side script that's currently in the Href
attribute moved to the Onclick
attribute. If we could just go in and tinker with the LinkButton class's
AddAttributesToRender(HtmlTextWriter)
method we'd simply add
writer.AddAttribute(HtmlTextWriterAttribute.Href, "#");
and change the existing AddAttribute()
lines to specify as their first parameter HtmlTextWriterAttribute.Onclick
. However, we can't directly modify this
method - we can only extend it.
Let's take the skmLinkButton
class that was examined in an earlier 4Guys article, Displaying Text in the
Browser's Status Bar When Mousing Over a LinkButton. If you'll take a moment to check out that class, you'll see that
it's AddAttributesToRender(HtmlTextWriter)
method has the following code:
|
Recall that skmLinkButton adds two additional features to the built-in LinkButton control:
- The ability to display text in the status bar when the user mouses over the link (this functionality is added via the second conditional statement), and
- The ability to add a client-side confirm dialog box. This logic is handled in the first conditional.
AddAttributesToRender(HtmlTextWriter)
method to the following:
|
There are two problems with the above, proposed additions. First, the Util.GetClientValidatedPostback()
method is internal to the System.Web.dll
assembly, meaning that we cannot create our own class that
calls it. Thankfully, we have Reflector handy, so we can merely copy the code from this method to the skmLinkButton
class (it's a mere couple of lines of code in total). In fact, we'll need to tweak the code for the
Util.GetClientValidatedPostback()
method just a bit, as we'll see momentarily.
The other challenge is that using the above code, the skmLinkButton will still emit its JavaScript in the Href
attribute. This is because the base class's AddAttributesToRender(HtmlTextWriter)
method adds this
Href
value first (as we're calling base.AddAttributesToRender(writer)
before doing our
own writer.AddAttribute(HtmlTextWriterAttribute.Href, "#");
. When rendering the <a>
element,
the HtmlTextWriter
will use the first attribute value (the script emitted from the base class's
AddAttributesToRender(HtmlTextWriter)
method) rather than the second value (the #
added from
the skmLinkButton's AddAttributesToRender(HtmlTextWriter)
method). The solution is easy enough - call
the base class's AddAttributesToRender(HtmlTextWriter)
method after we've already set the Href
attribute from skmLinkButton. (Personally this feels like a serious hack, but I do not see any other way around, as the
HtmlTextWriter
class doesn't have any methods to remove existing attribute values...)
The final challenge is making sure that the script for the client-side confirm (if present) is also included in the
Onclick
attribute in the right place. These challenges are all resolved by using the following code:
|
One small, but important, change worth noting: previously if there were no validation controls on the page or if the
LinkButton's CausesValidation
property was false, the script injected into the Href
attribute
was returned by a call to Page.GetPostBackClientHyperlink(this, "")
. The problem with this method is that
it prepends the javascript:
markup to the call to __doPostBack(...)
. This works fine if we are
not adding our own call to confirm()
, but if we are, the created script will look something like:
javascript:if confirm('...') javascript:__doPostBack(...)
. Note the second, superfluous javascript:
in there, preceeding the call to __doPostBack(...)
. IE doesn't seem to mind this, but FireFox, for example,
raises a client-side script error.
To remedy this, we can replace the call to Page.GetPostBackClientHyperlink(this, "")
with
Page.GetPostBackEventReference(this, "")
, which simply returns the proper call to __doPostBack(...)
without prepending javascript:
.
Using skmLinkButton in an ASP.NET Page
The previous article, Displaying Text in the Browser's Status Bar When Mousing Over a LinkButton, discusses how to use skmLinkButton in an ASP.NET page. With this addition, there are no code changes necessary for using skmLinkButton.
A live demo of the new, enhanced skmLinkButton is available at http://scottonwriting.net/demos/skmLinkButtonDemo.aspx. Note that when SHIFT+clicking on a LinkButton in the live demo, or when right-clicking on the LinkButton and choosing to open the link in a new window, the resulting new window simply reloads the existing page. It doesn't cause a JavaScript error or show a blank page, as is the case with the built-in LinkButton.
Happy Programming!
Attachments: