0

I have a Visualforce error that's driving me crazy. My Javascript file ui.js (see blow) is just defining an onclick event on a DOM element. Here is my javascript :

(function (window, document) {
    var layout   = document.getElementById('layout');
    var menu     = document.getElementById('menu');
    var menuLink = document.getElementById('menuLink');

    function toggleClass(element, className) {
        var classes = element.className.split(/\s+/),
            length = classes.length,
            i = 0;

        for(; i < length; i++) {
          if (classes[i] === className) {
            classes.splice(i, 1);
            break;
          }
        }
        // The className is not found
        if (length === classes.length) {
            classes.push(className);
        }

        element.className = classes.join(' ');
    }

    function clickOnMenu (e) {
        var active = 'active';
        e.preventDefault();
        toggleClass(layout, active);
        toggleClass(menu, active);
        toggleClass(menuLink, active);
    };

    menuLink.onclick = clickOnMenu; // Error here

}(this, this.document));

The line causing the error is: menuLink.onclick = clickOnMenu; The error shown in chrome is TypeError: Cannot set property 'onclick' of null.

I don't understand why the menuLink variable is null. In Chrome console, when I type console.log(menuLink), the variable is not null and contains the DOM object in question.

I included my javascript file at the end of the visualforce page :

Note that the exact same code works fine on a local environment! (html instead of visualforce).

Thanks in advance for any help. Riadh

1

The error message which you are receiving means that at the point when this code is executed, there is no element rendered in the browser that can be found with the id of menuLink. When you manually execute the statement in the browser console, you're doing so after the entire page has been rendered and menuLink is now in the page context. You will probably find that this script is executing before the DOM is fully loaded even though you've got it at the end of the VF markup. (What tags did you use to do this? <apex:includeScript?)

If you're using <apex: id="yourName" tags for these elements now or in the future, the following may be useful.

Salesforce documentation about accessing the Id as it was rendered in the DOM.

Best Practices for Accessing Component IDs

You don't mention where this ui.js file is stored but depending on the answer to that question, this answer may need to be adjusted to pass references rather than using merge field syntax (which only works within the VF page itself).

If you need to get the generated unique id for the node on the page, you should use the {!$Component.yourTagID} merge field syntax to do so. This mechanism will give you the full id attribute of the element in the page as it was rendered in the browser.

If the path to the component cannot be found as you wrote it, your path is likely incorrect or ambiguous in the page context. It will not throw an error when this happens, it will just return an empty string. i.e. var myVar = ''; where you expected var myVar = 'j_id0:j_id2:myThing'; You can verify what's happening by looking at the source in the browser.

For instance:

(function (window, document) {
    var layout   = document.getElementById('{!$Component.layout}');
    var menu     = document.getElementById('{!$Component.menu}');
    var menuLink = document.getElementById('{!$Component.menuLink}');

or something more targeted to a specific path from the <apex:page id="myPage"> to the element itself:

(function (window, document) {
    var layout   = document.getElementById('{!$Component.myPage.myForm.myPageBlock.layout}');
    var menu     = document.getElementById('{!$Component.myPage.myForm.myPageBlock.menu}');
    var menuLink = document.getElementById('{!$Component.myPage.myForm.myPageBlock.menuLink}');

or if you want to pass the DOM id as a function param:

(function (window, document, layoutId, menuId, menuLinkId) {
    var layout   = document.getElementById(layoutId);
    var menu     = document.getElementById(menuId);
    var menuLink = document.getElementById(menuLinkId);

    // check for nulls

    // content trimmed for brevity

}(this, this.document, '{!$Component.layout}', '{!$Component.menu}', '{!$Component.menuLink}'));
| improve this answer | |
  • Thank you for your answer! But the thing is I'm not using Visualforce tags, except for <apex:page>. It's a full HTML5 app. So I'm just using simple HTML elements for everyting: menuLink is a link (<a> element), "menu" and "layout" are div elements. I'm not using apex:form, I will just use javascript remoting/remote objects for business logic. To answer your quetions : I'm using <apex:includeScript>, but <script src=""> gives me the same result. Finally, My ui.js file is stored in Salesforce static resources. Thanks. – Riadh Mankai Jun 16 '15 at 4:50
0

Visualforce overrides the Id you set during development. For instance, if you write

<apex:inputField id="test" value="{!oOpportunityModel.closeDate}" />

during development, you'll get

<input id="j_id0:j_id2:j_id3:j_id32:test" name="j_id0:j_id2:j_id3:j_id32:test" type="text">

when the page renders (j_id) depends on in how much containers you set your tag (pageBlock, pageBlockSection, pageBlockSectionItem, etc.)

So you may wanna use class instead, so that you can just use

var menuLink = document.getElementsByClassName('menuLink')[0];

That should do the job. I hope.

| improve this answer | |
  • Thanks Julien, but the problem is that I'm not using visualforce elements for my page. Except for apex:page to wrap the whole page, I'm only using simple html5 elements. – Riadh Mankai Jun 16 '15 at 4:56
0

Problem solved. I just deleted the first line: (function (window, document)... But I don't really understand what the problem is. Thank you.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.