Adding aria-describedby to ui:inputText in Lightning Components

If you look at the original Aura open source project there are attributes in the ui:input component for ariaDescibedBy. But neither ui:input or the aria attribute seem to have made the jump to global scope to be surfaced in the Lightning Component framework on the platform.

Your best bet to get to the DOM is to make changes using a renderer.

To familiarize yourself with the component rendering lifecycle, I'd suggest taking a look at the documentation. But the short version goes like this:

  • Init should be used primarily for getting data into place that your component will need to work. But happens before the DOM is available.
  • The renderer is something every component has, and is what gets used to turn the component into some kind of HTML fragment to be inserted into the DOM.
  • There are events you can override like render, afterRender, and reRender.
  • Anything you want to do that touches the DOM should be placed here and not in an event handler (in other words a controller), as this can create race conditions (done it...ain't pretty).

Typically you want to do the following:

({
  //the name of the function has to be this
  afterRender: function(component, helper) {
    //make sure you do the default render behavior...
    //this ensure you get the component you actually want, like an 
    //input, or button...without it, you get nothing
    this.superAfterRender();

    //call a helper function that actually renders your DOM object
    helper.addAria(component);
  }
})  

In my case, I was drawing on an HTML canvas. Putting the doChart function here made sure the canvas html element had actually been created before I went to get canvas context. This could be the problem your having.

In your case, you'd need to get the actual HTML element, and then set the HTML attribute. Something like this:

addAria : function(component){

  //get the actual HTML DOM element for this component
  var thisComponent = component.getElement(); 

  //create an attribute;
  var ariaAttr = document.createAttribute("aria-desribedby"); 

  //set the value of my attribute
  ariaAttr.value = "expnameHelp"; 

  //set the attribute into the DOM element
  thisComponent.setAttributeNode(ariaAttr); 
}

Disclaimer: untested code above

It is important to remember the difference between a lightning component attribute, and an HTML DOM attribute.

The lightning component attribute sometime will have to do with how the component is rendered...but is not necessarily what the component will look like once rendered into the DOM of the page.

HTML attributes are actual DOM data, and will be what the browser interprets to create the HTML page you eventually display.

Anytime you are doing component.get('v.someAttribute') this is the lightning component attribute.

Anytime you are doing component.getElement() you will have a handle to the actual HTML elements of your page...but this is only meant to work from the context of the renderer.


As a heads up this direct reaching inside of a component's internals that you do not own is not supported and with the upcoming security model enhancement called LockerService this code will no longer even be possible.

This line will fail:

var expnameElm = component.find('expname').getElement();

and even if you tried to go directly to the DOM you are going to be blocked by the security infrastructure that applies fine grained object capability locking to the DOM (and all Lightning APIs too).

because only the ui:inputText should depend on its internal private parts otherwise there is no way for the component's author to maintain API compatibility release over release. This is the tradeoff for having push upgrades in an enterprise grade environment - everything needs to be formal API contracts or there is no way that Salesforce can guarantee backward compatibility.

I have brought this to the attention of the folks that own the ui:visible interface that defines v.ariaDescribedBy to see if they can add the access="GLOBAL" to this to make it visible so your original approach of just setting ariaDescribedBy= would work.