The $Component notation is quite picky about the correct number of prefixes, so if your amount, lc1 elements are nested inside other Visualforce components (such as pageblocks, datatables, outputpanels) you need to include the full hierarchy to get the correct name. I used to build this up in baby steps starting with the outermost container and check the page source each time to see if the $Component rendered anything (meaning it was a valid path) or if it was blank (meaning invalid).
These days I use a JQuery selector to find the element that ends with the particular id - that way I don't have to worry about the location prefixes and I can move elements around the page without having to rebuild the "fully qualified" name.
I include JQuery from a CDN, but you could load it as a static resource :
<apex:includescript
value="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" />
and then pass the id string literals to the JavaScript function :
<apex:outputLink value="javascript:compute('Amount', 'lc1', ... etc);
Compute
</apex:outputLink>
then build up JQuery selectors based on the literal ids:
<script>
function compute(amount, lc1, lc2, lc3, lc4, lc5, lc6) {
/* Compute the loan charges depending on the loan amount here */
$('[id$=' + lc1 + ']').val(6);
$('[id$=' + lc2 + ']').val(6);
... etc
}
</script>
The key aspect of the selector is the '[id$=' + lc1 + ']' this translates to 'the element with an id that ends with the contents of the lc1 variable'. I've also used the JQuery val() method to set the value of the element.
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}'));
Best Answer
In this case, it was a simple typo: the ID value is cAsE-sEnSiTiVe.
Always make sure you're matching the case for your Id values.
As a more practical solution, consider using $Component:
Using $Component will resolve the element Id correctly (in a case-insensitive manner) so you don't have to worry about it as much.