Why is this so DOM Hard?

August 11th, 2010

Let me start out right away by saying I am not a DOM or CSS master. I’ve typically had employees or co-workers to perform these functions for my so I’ve (unfortunately) let me skills wane. However, I’m not totally clueless. I was trying to build the following Visualforce page a couple of days ago and working with the DOM and Visualforce components seems to be much harder than what it should be. Perhaps I’m doing something wrong? Luckily Wes Nolte and Joel Dietz are experts and they both have a number of great blog posts on this subject.

I was trying to create the following Visualforce page that contains a simple SelectList (I’ve sanitized the code for your protection).

When the user submits the form, the value of the currently selected option is fired off via JavaScript. Shouldn’t be a big deal, right? Based upon the Visualforce docs for Using JavaScript to Reference Components, I thought this code would work using the $Component variable (line #5).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<apex:page controller="MyController" showHeader="false" id="thePage">
 
  <script language="javascript">
  function doSubmit() {
    alert(document.getElementById("{!$Component.theSelectList}").value);
  }
  </script>
 
  <apex:form id="theForm">
    <apex:pageblock id="thePageBlock">
      <apex:pageblockSection id="thePageBlockSection">
        <apex:pageBlockSectionItem id="thePageBlockItem">
          <apex:selectList value="{!selectedContentId}" size="1" id="theSelectList">
            <apex:selectOptions value="{!contentOptions}"/>      
          </apex:selectList>
        </apex:pageBlockSectionItem>
        <input type="button" value="Submit" onclick="doSubmit()" />
      </apex:pageblockSection>
    </apex:pageblock>
  </apex:form>
 
</apex:page>

Unfortunately Firebug choked with s null pointer for the object’s reference.

I found Wes’ great post, VisualForce Component Ids & Javascript, and took a look at the element Id that Salesforce generated for the SelectList component. Based upon the other elements in the DOM hierarchy, I hard-coded my JavaScript to use the id that Salesforce was generating and the method worked correctly!

3
4
5
6
7
  <script language="javascript">
  function doSubmit() {
    alert(document.getElementById("thePage:theForm:thePageBlock:thePageBlockSection:thePageBlockItem:theSelectList").value);
  }
  </script>

So now I was confused. I dug deeper into Wes’ post, the code and comments. Wes’ solution was to assign the element value to a JavaScript variable just after it appears on the page (or at the same level within the page tree). My final (working) code looks like the following. Notice line #16 where I set the local JavaScript variable and then line #5 where I reference it’s currently selected value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<apex:page controller="MyController" showHeader="false" id="thePage">
 
  <script language="javascript"> 
  function doSubmit() {
    alert(options.value);
  }
  </script>
 
  <apex:form id="theForm">
    <apex:pageblock id="thePageBlock">
      <apex:pageblockSection id="thePageBlockSection">
         <apex:pageBlockSectionItem id="thePageBlockItem">
            <apex:selectList value="{!selectedContentId}" size="1" id="theSelectList">
              <apex:selectOptions value="{!contentOptions}"/>       
            </apex:selectList>
            <script> var options = document.getElementById("{!$Component.theSelectList}"); </script>
         </apex:pageBlockSectionItem>
        <input type="button" value="Submit" onclick="doSubmit()" />
      </apex:pageblockSection>
    </apex:pageblock>
  </apex:form> 
 
</apex:page>

Wes states that this solution is simple as well as flexible. However, what happens if you have 20+ form fields that you need to process in this manner? Does accessing the DOM seem too hard with Visualforce components? Let me know if I am doing something wrong?

Categories: Salesforce, Visualforce

Leave a comment

Comments Feed16 Comments

  1. osama

    Your original code would have worked if you write the javascript function inside apex:form

  2. Wes

    It is a bit hacky innit? I’ve thought of other ways to do this, depending on the situation, some are hackless but other are just a touch less hacky. For example, you can set onblur (or some event) handlers on the fields and have them assign “this” to JS vars, not very OOP though.

    The real problem is not the actual ID, but the method of reference. Unless SF changes this I *think* we’ll always need a hack or lib. I’d be glad to be shown otherwise though.

  3. Jeff Douglas

    @Osama Just tried moving the javascript inside the form tag and got the same null pointer result.

  4. Abhinav

    The point you raised is a pain for sure !

    What I switched to and using right now is “CSS Selectors”, all javascript frameworks support that. They make life simple on many occasions.

  5. Ryan Esposto

    Yes, this is extremely inconvenient.

    I wrote a small JS selector method — similar to prototypes $(elementId) method — that basically builds an index (on a per-page-load basis) of all of the elements on a page keyed off of the “actual” DOM element IDs — and by “actual”, I mean anything that you supply to the “id” parameter of a tag, whether it is a VF component or a standard HTML element.

    It works fine on smaller pages, and while it doesn’t scale to huge pages very well, it makes writing the procedural JS that does what you ultimately set out to do much simpler.

  6. Jason

    Ya, there isn’t a great way. I think this should, but currently does not, work: document.getElementById(“{!$Component.theSelectList}”)

    Ids on apex components must be unique so why the heck can’t the Visualforce compiler know what element you are talking about and merge the correct id. Having to define the entire hierarchy seems silly. I should create an Idea.

  7. Jeff Douglas

    @Jason Yes you should!!

  8. Sanket

    I am not able to post here.

  9. Sanket

    I have one code to explain the above problem with the solution.
    But when i m trying to post it is saying duplicate comment and still i cannot see the comment i and code i posted

  10. Sanket

    HI jeff,
    The {!$Component.Object_name} here the component takes the value or it references all the object inside which it

    is defined. So it means that if the object is inside form element go down the hierarchy after form. You need to include all

    the elements after that. That means in your case you need to use like this
    alert(document.getElementById(‘{!$Component.theForm.thePageBlock.thePageBlockSection.thePageBlockItem.txt}’).value);
    This mean you need to follow all the hierarchy.

  11. Sanket

    Correction in the above code instead of txt which i used as name it should be the selectlist name
    alert(document.getElementById(‘{!$Component.theForm.thePageBlock.thePageBlockSection.thePageBlockItem.theSelectList}’).value);

  12. osama

    I don’t it didnt work at your end. I have used this thing in a lot of pages.

    I will try again

  13. Joel Dietz / d3developer

    Almost all Visualforce elements accept a styleClass which can be used as a selector.

    jQuery selector:

    $(‘.textfield1′);

    This is a hair slower than grabbing the DOM element by id but much cleaner so I always use it.

  14. Joel Dietz / d3developer

    Looks like it ate it again

    < apex : inputText styleClass = “textfield1″ / >

  15. Jeremy Ross

    Not only does visualforce generate godawful IDs, but the $Component variable meant to ease the pain is broken. Hey, log it as an Idea!

  16. Mauricio

    Hi all

    Sometimes what i do is to pass the id as a parameter to the visualforce, so i do not have to worry about it. On the other hand, not always you can pass it, in that case styleClass is the best.

    There is another work around which is even worst that all, and its to work down all the children, I have seen it done. So if you are desperate it does work.

Leave a comment

Feed

http://blog.jeffdouglas.com / Why is this so DOM Hard?

WordPress Appliance - Powered by TurnKey Linux