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!

Comments (7)

  1. Mascha says:

    Die ganze Sache wird etwas komplizierter, wenn man Labels oder Textboxen in einem UserControl dynamisch erzeugt und dieses wiederum dynamisch zur Laufzeit läd. Wie Peter Bucher bereits geschrieben hat, ist die ClientID abhängig von den umgebenden Containern und daher zum Zeitpunkt der Erstellung noch nicht hinreichend bekannt. Für alle die wie ich mit diesem Problem zu kämpfen haben: die Lösung heißt OnPreRender! Wenn man die ClientID hier abfragt erhält man die tatsächliche ClientID.

    Happy Coding!

    Viele Grüße

    Mascha

  2. Hallo Daniel,

    leider beschreiben alle Lösungsansätze nie den Fall, wie man die ClientID gescheit ermittelt, wenn man den Javascriptcode auslagert in  externen JS-Files und von dort aus drauf zugreifen möchte.

    Hier wäre schön, wenn in einer Frameworkversion mal eine gescheite allgemeine Lösung mit implementiert werden würde.

    Ich selbst löse dieses Problem über ein sogenantes Clientobjekt, welches ich zB im LoadComplete der Page erzeuge und via JSON zum Client rausschreibe.

    Gruß

    Rene

  3. Wenn man sicher ist, dass das Control wirklich eine eindeutige ID hat, kann man es trotzdem mit Javascript finden. Das geht dann auch wenn der JavaScript code in *.js Dateien ausgelagert ist. Siehe z.B. mein Post:

    http://peter.hahndorf.eu/blog/2008/05/09/getElementByIdProblemsInAspnet.aspx

  4. Peter Bucher says:

    Wie den meisten schon bekannt sein wird, erzeugt ASP.NET ClientIDs die auf dem Client genutzt werden

  5. Holger Matthias Rößler says:

    Hi@all,

    Danke für die Tipps.

    Dieses Verhalten ist Clientseitig ziemlich nervend und auch unerwünscht.

    ASP.NET soll schauen, dass es Serverseitig "flutscht".

    Eindeutige IDs für JS habe ich auch ohne/vor ASP.NET hinbekommen!

    Nochamls vielen Dank.

    Grüße

    Holger Matthias Rößler