Wie referenziere ich in JavaScript ein Control dessen Namen durch INamingContainer geändert wurde?

Was ist das Problem?

Vielleicht haben Sie schon bemerkt, dass clientseitige IDs von Controls gelegentlich von den ursprünglich vergebenen IDs abweichen. Dieses Phänomen tritt immer dann auf, wenn Controls in Container eingefügt werden, die das Interface INamingContainer implementieren. Anbei ein kleines Beispiel:

<

asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> <asp:TextBox ID="TextBox1" runat="server" /> <br /> <asp:Button ID="Button1" runat="server" Text="Button" /></asp:Content>

In diesem Beispiel wurden zwei Controls in einen ContentPlaceHolder eingefügt woraus ASP.NET den folgenden Markup Code erzeugt:

<div>    <input name="ctl00$ContentPlaceHolder1$TextBox1" type="text" id="ctl00_ContentPlaceHolder1_TextBox1" />    <br />    <input type="submit" name="ctl00$ContentPlaceHolder1$Button1" value="Button" id="ctl00_ContentPlaceHolder1_Button1" /></div>

Problematisch wird es nun, wenn Sie versuchen in JavaScript über die ID des Controls auf das Control zuzugreifen. Da die ID des Controls von ASP.NET geändert wurde findet getElementById in dem folgenden Beispiel kein Control und der Code resultiert in einem Scriptfehler (Fehler 21, Objekt erforderlich).

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">    <script type="text/javascript">        function SayHello() {            alert("Hallo " + document.getElementById("TextBox1").value);        }     </script>

    <asp:TextBox ID="TextBox1" runat="server" />     <br />     <asp:Button ID="Button1" runat="server" Text="Button" OnClientClick="SayHello();" />

</asp:Content>

Warum verhält sich ASP.NET so?

In dem Beispiel ändert ASP.NET den Namen der Controls um eindeutige IDs auf dem Client zu garantieren. Befände sich beispielsweise auf der Seite ein weiterer ContentPlaceHolder mit einer TextBox mit gleicher ID käme es zu einem Namenskonflikt. Verantwortlich für diese Änderung ist das Interface INamingContainer. Wenn ein Control INamingContainer implementiert (in diesem Fall ContentPlaceHolder1) erzeugt ASP.NET UniqueIDs für jedes ChildControl (in diesem Fall TextBox1 und Button1) indem die UniqueID des NamingContainers und die UniqueID des ChildControls mit einem Doppelpunkt verknüpft werden. Um clientseitig auf ein Control zugreifen zu können gibt es die ClientID die ebenso wie die UniqueID eindeutig für eine Webseite ist. Die ClientID ist die "scriptfreundliche" Version der UniqueID und verwendet im Gegensatz zu der UniqueID einen Unterstrich als Trennzeichen.

Was ist die Lösung?

Neben der Möglichkeit die voraussichtliche ClientID im Vorfeld in den JavaScript Code einzufügen (nein, das wollen Sie NICHT!!!) können Sie die ClientID über DataBinding Inline Code in Ihren Code überführen:

<script type="text/javascript">

    function SayHello() {         var con = document.getElementById("<%= TextBox1.ClientID %>");         alert("Hallo " + con.value);     }

</script>

Alternativ haben Sie übrigens auch die Möglichkeit die ClientID über ClientScript.RegisterClientScriptBlock auf der Seite verfügbar zu machen:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    Dim js As String = "function GetTextBox1() { return document.getElementById('" & TextBox1.ClientID & "'); }"     ClientScript.RegisterClientScriptBlock(Me.GetType, "TextBox1_ClientID", js, True)

End Sub

Happy Coding!

Daniel

20071018 - DataBinding Inline Code; Danke Hannes!