2

I'm desperate having spent well over an hour trying to troubleshoot this. I am trying to access a node in the DOM which is created from an ASP.NET control. I'm using exactly the same id and I can see that they match up when looking at the HTML source code after the page has rendered. Here's my [MODIFIED according to suggestions, but still not working] code:

ASP.NET Header

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
    <script type="text/javascript">
    $(document).ready(
        var el = document.getElementById('<%= txtBox.ClientID %>');
        el.onchange = alert('test!!');
    )
    </script>
</asp:Content>

ASP.NET Body

<asp:TextBox ID="txtBox" runat="server"></asp:TextBox>

Resulting Javascript & HTML from above

<script type="text/javascript">
$(document).ready(
    var el = document.getElementById('MainContent_txtBox');
    el.onchange = alert('test!!');
)
</script>
...
<textarea name="ctl00$MainContent$txtBox" id="MainContent_txtBox"></textarea>

I can only assume that the script is loading before the control id has been resolved, yet when I look at the timeline with Chrome's "Inspect Element" feature, it appears that is not the case. When I created a regular textarea box to test and implement the identical code (different id of course), the alert box fires.

What on earth am I missing here? This is driving me crazy >.<

EDIT: Wierd code that works, but only on the initial page load; firing onload rather than onchange. Even jQuery says that .ready doesn't work properly apparently. Ugh!!

$(document).ready(function() {
    document.getElementById('<%= txtBox.ClientID %>').onchange = alert('WORKING!');
})
2
  • 2
    Aside from the document-ready issues, the syntax of your onclick is incorrect. You need to assign onclick to point to a function, not call it as if it was a function. So try changing it to something like el.onclick = function() { alert("test"); };, or you can do el.onclick = myFunction; (note no brackets after the name of your function) where elsewhere on the page you've defined function myFunction() { ... }. Commented Jul 22, 2011 at 1:02
  • Ack!! I'm tired for today. I'll pick this up again tomorrow and determine an answer then. Thanks guys ;) Commented Jul 22, 2011 at 1:37

3 Answers 3

5

Assuming the rendered markup does appear in that order, the problem is that the element doesn't yet exist at the time your JavaScript is attempting to locate it.

Either move that JS below the element (preferably right at the end of the body) or wrap it in something like jQuery's document ready event handler.

Update:

In response to your edits, you're almost there but (as others have mentioned) you need to assign a function to the onchange event, not the return result of alert(). Something like this:

$(document).ready(function() {
  // Might as well use jQuery to attach the event since you're already using
  //  it for the document ready event.
  $('#<%= txtBox.ClientID %>').change(function() {
    alert('Working!');
  });
});

By writing onchange = alert('Working');, you were asking JavaScript to assign the result of the alert() method to the onchange property. That's why it was executing it immediately on page load, but never actually in response to the onchange event (because you hadn't assigned that a function to run onchange).

Sign up to request clarification or add additional context in comments.

4 Comments

I will give that a try and get back to you. Thanks so much for the quick response :)
Ok, I got it working by moving the script to the bottom (that'll work for now), however, apparently the HTMLTextArea DOM object doesn't have an onclick event according to Chrome Inspect Element. The event I actually want this code to fire on, is when the contents of the textbox are changed.
@Chiramisu - textareas definitely have a click event, but your syntax is incorrect. See my comment attached to your original question. If you want to know about when the contents change you probably want to use onchange rather than onclick (noting that onchange isn't fired as you type, only when the element loses focus; you can trap onkeyup or onkeypress to look for changes as they happen).
@Dave Ward: I finally got it thanks to you. I appreciate you hanging in there with me while I worked through it. I'm starting to get a bit more understanding as to how jQuery works. Interesting syntax. Pretty easy to get lost in all the () & {} haha. Cheers ;)
2

Pick up jQuery.

Then you can

$(function()
{
    var el = document.getElementById('<%= txtBox.ClientID %>');
    el.onclick() { alert('test!!'); }
});

7 Comments

1) suggest's OP uses a sizeable javascript library with very powerful selector and event-handling support, 2) uses a single feature to wrap vanilla JavaScript code that would benefit from JQuery :)
Does it have to be in a function? This is code I want to execute no matter what.
@tomfumb - Who woulda thunk it?
@Chiramisu the function wrap just means that the code won't be executed until the document is ready - so all of your elements are guaranteed to exist in the DOM (though they will not necessarily be populated if they contain images or other external content). This is different to putting code in the document body's onload event handler as this won't be executed until all external content has been retrieved.
Got it... Still can't figure out which event to use though. I'm pretty sure I'm doing it right, now, I think. SO frustrating :(
|
1

Other answers have pointed out the error (attempting to access DOM nodes before they are in the document), I'll just point out alternative solutions.

Simple method

Add the script element in the HTML below the closing tag of the element you wish to access. In its easiest form, put it just before the closing body tag. This strategy can also make the page appear faster as the browser doesn't pause loading HTML for script. Overall load time is the same however, scripts still have to be loaded an executed, it's just that this order makes it seem faseter to the user.

Use window.onload or <body onload="..." ...>

This method is supported by every browser, but it fires after all content is loaded so the page may appear inactive for a short time (or perhaps a long time if loading is dealyed). It is very robust though.

Use a DOM ready function

Others have suggested jQuery, but you may not want 4,000 lines and 90kb of code just for a DOM ready function. jQuery's is quite convoluted so hard to remove from the library. David Mark's MyLibrary however is very modular and quite easy to extract just the bits you want. The code quality is also excellent, at least the equal of any other library.

Here is an example of a DOM ready function extracted from MyLibrary:

var API = API || {};

(function(global) {

  var doc = (typeof global.document == 'object')? global.document : null;

  var attachDocumentReadyListener, bReady, documentReady,
      documentReadyListener, readyListeners = [];
  var canAddDocumentReadyListener, canAddWindowLoadListener,
      canAttachWindowLoadListener;

  if (doc) {
    canAddDocumentReadyListener = !!doc.addEventListener;
    canAddWindowLoadListener    = !!global.addEventListener;
    canAttachWindowLoadListener = !!global.attachEvent;

      bReady = false;
      documentReady = function() { return bReady; };
      documentReadyListener = function(e) {
        if (!bReady) {
          bReady = true;
          var i = readyListeners.length;
          var m = i - 1;
          // NOTE: e may be undefined (not always called by event handler)
          while (i--) { readyListeners[m - i](e); }
        }
      };

      attachDocumentReadyListener = function(fn, docNode) {
        docNode = docNode || global.document;
        if (docNode == global.document) {
          if (!readyListeners.length) {
            if (canAddDocumentReadyListener) {
              docNode.addEventListener('DOMContentLoaded',
                                        documentReadyListener, false);
            }
            if (canAddWindowLoadListener) {
              global.addEventListener('load', documentReadyListener, false);
            }
            else if (canAttachWindowLoadListener) {
              global.attachEvent('onload', documentReadyListener);
            } else {
              var oldOnLoad = global.onload;              
              global.onload = function(e) { 
                  if (oldOnLoad) { 
                    oldOnLoad(e); 
                  }
                  documentReadyListener();
              };
            }
          }
          readyListeners[readyListeners.length] = fn;
          return true;
        }
        // NOTE: no special handling for other documents
        // It might be useful to add additional queues for frames/objects
        else {
          if (canAddDocumentReadyListener) {
            docNode.addEventListener('DOMContentLoaded', fn, false);
            return true;
          }
          return false;
        }
      };

      API.documentReady = documentReady;
      API.documentReadyListener = documentReadyListener;
      API.attachDocumentReadyListener = attachDocumentReadyListener;

  }
}(this));

Using it for your case:

function someFn() {
  var el = document.getElementById('MainContent_txtBox');
  el.onclick = function() { alert('test!!');
}

API.attachDocumentReadyListener(someFn);

or an anonymous function can be supplied:

API.attachDocumentReadyListener(function(){
  var el = document.getElementById('MainContent_txtBox');
  el.onclick = function() { alert('test!!');
};

Very simple DOM ready functions can be done in 10 lines of code if you just want one for a specific case, but of course they are less robust and not as reusable.

2 Comments

+1 For the MyLibrary link. I wasn't aware of that library, but it looks good.
It's a great resource, and written specifically to be easy to modularise.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.