2

In current SF release lightning component.find(…).getElement is not working. I understand this due to Locker service which is blocking DOM elements from getting accessed.

So is there any better way we can access the attributes of elements using other lightning methods ?

  • Can you provide a code example of exactly what you are trying to do? The excellent info from @Sumuga below is spot on but I'd like to also look at showing you how to accomplish what you want at the component level instead of dropping to trying to access parts of the DOM you do not own/control (creates an implicit and unmaintainable dependency that the author of the component in question has not agreed to). – Doug Chasman Nov 10 '16 at 12:52
  • Sumuga and Doug Thanks for the response. @Doug I am trying to get ID of DIV and remove its class. I am having this method in helper class, which is getting called from the controller: methodABC: function(component) { var divElement = component.find('divID').getElement(); $A.util.removeClass( divElement ,'div-hide'); component.set('v.DisableButtons', true); } – nevrekar_amey Nov 10 '16 at 19:15
  • What you describe should work provided the <div> is contained in a component in your namespace - on the surface this is exactly what we do all the time. I'll post a complete working example as an answer so you can hopefully let me know what is different about your code. – Doug Chasman Nov 10 '16 at 21:16
6

Here is a working example .app of what I think you are saying is not working for you (NOTE TO MODERATORS: I did not use a comment because they do not provide enough formatting capabilities to handle code etc).

@nevrekar_amey Please provide something similar that showcases the error case you are seeing.

<aura:application>
    <div aura:id="divID" class="blue">Hi</div>
    <lightning:button label="Remove Class" onclick="{!c.removeClass}"/>
</aura:application>

controller:

({
    removeClass : function(component, event, helper) {
        helper.removeClassHelperMethod(component);
    }
})

helper:

({
    removeClassHelperMethod : function(component) {
        var divElement = component.find('divID').getElement(); 
        $A.util.removeClass(divElement, 'blue'); 
    }
})

css:

.THIS.blue {
    background-color: blue;
}

Or an even better approach that does not run the risk of colliding with Lightning's stewardship of the DOM elements its managing on your behalf:

<aura:application>
    <aura:attribute name="class" type="String" default="blue" access="private"/>
    <div aura:id="divID" class="{!v.class}">Hi</div>
    <lightning:button label="Toggle Class" onclick="{!c.toggle}"/>
</aura:application>

({
    toggle : function(component, event, helper) {
        component.set("v.class", component.get("v.class") === "" ? "blue" : "");
    }
})

Even better - this approach is also guaranteed to work in your init scenario too!

<aura:application>
    <aura:attribute name="class" type="String" default="blue" access="private"/>
    <aura:handler name="init" value="{!this}" action="{!c.init}"/>

    <div aura:id="divID" class="{!v.class}">Hi</div>
    <lightning:button label="Toggle Class" onclick="{!c.toggle}"/>
</aura:application>

({
    init : function(component, event, helper) {
        component.set("v.class", "green");
    },

    toggle : function(component, event, helper) {
        component.set("v.class", component.get("v.class") === "" ? "blue" : "");
    }
})

.THIS.blue {
    background-color: blue;
}

.THIS.green {
    background-color: green;
}
6

The Locker Service Critical update is a virtual security layer added by Salesforce to prevent XSS issues and to prevent components from reading other component's layout without any restrictions.

The getElement() will work fine if you are accessing any DOM element within your namespace (c:) or (yournamespace:) .. Meaning take the following example..

Locker App

This App has 2 inputs - one with ui:inputText and other with traditional html input. Both Render as html inputText in the front end, however the namespace differs. The first one falls under the ui: namespace and the later falls under your custom namespace (c:) in my case or (yourcustomnamespace:) if you have configured one.

Accessing Elements in Lightning Components :

Generally we access the components and its attributes as follows

var cmp = component.find("theauraid");

If you console.log this Object returned by ui:namespace will have no method or property called as getElement(), thus causing the aura error when the application is loaded. This is because locker service security does not want you to access the element root of a ui:namespace from your custom namespace. ( c: --> ui: )

Accessing the Elements using javascript methods :

The ui:inputText will get translated as follows in the front-end.

UI Rendering

The id of the inputElement "3:0" is the globalID, which can be retrieved as follows

var globalId = component.find("theauraid").getGlobalId();

And if we do Something like document.getElementById(globalId);, this will also return null, because these execution context virtually differs. The Salesforce authored components run in System Mode ( the ui: namespace for instance ) and the code that you write inside the controllers run in USER MODE. Hence you will not have access to the REAL DOM WINDOW. This is what prevents you from acessing other namespaces.

DO look into the following example for a detailed description on locker Services.

Reference : Locker Service Critical Update

Alternative :

Hence if you want to manipulate the elements, and if you wish to work in sync with the locker service critical update. You will have to use elements that works in the local namespace. For instance.

Locker example

Use your custom HTML mark-up to get the elements in order to manipulate the same. All the properties will be similar to aura:html, do look into the aura:html reference for accessing values and propagating events.

Reference : aura:html reference

0

Hi Doug your above code suggestion totally works. Now I realize what I was doing wrong. I was trying to call removeClassMethod in init event something like below:(Stealing from Doug :) NOTE TO MODERATORS: I did not use a comment because they do not provide enough formatting capabilities to handle code etc)

(Note: Providing ex samples as the code is a production code)

`<aura:component>
<aura:handler name="init" value="{!this}" event="aura:doneRendering" action="{!c.removeClass}" />

<div aura:id="divID" class="blue">Hi</div>
<lightning:button label="Remove Class" onclick="{!c.removeClass}"/>
</aura:component>`

Now the above code was not working as I was trying to access the DOM at the init even before it was rendering. After I use aura:doneRendering event the error goes away.

The reason why I was calling method at the time of initialization was because I wanted to show some initial values on the page from controller.

Ex controller snippet would look like :

({

   doInit : function(component, event, helper) {
        helper.removeClassHelperMethod(component);
        var callbackMonitor = helper.getremoveClassHelperMonitor(component);
        helper.getSummary(component, callbackMonitor);
   }
})

But now what is happening is though the error does not occur, the summary is not been shown on the page and then when I click on a button which I have to navigate through the above summary pops up.

So I believe the doInit method is not been called when event="aura:doneRendering" is been set.

Ex helper code:

({
    removeClassHelperMethod : function(component) {
        var divElement = component.find('divID').getElement(); 
        $A.util.removeClass(divElement, 'blue'); 
    }
})
  • 1
    @sumuga Yes when LS is turned Off the the doInit method is working fine and also doneRendering works fine. Though as said earlier it fails when LS is turned ON. Now that you have raised the question it makes me think again, that even with LS turned OFF and if the DOM was not rendering completely in init it should have failed to get the element ? – nevrekar_amey Nov 11 '16 at 6:22
  • 2
    Neither init nor aura:doneRendering are good choices for this. aura:doneRendering is a global event that can and often does fire multiple times and is not linked to your specific component's rendering lifecycle. init is well documented as happening before you can guarantee the DOM has even been created for your component. The correct choice for this would be to move the code that needs the Lightning created DOM to exist into a renderer (or controller method that you insure can only be called after the DOM exists like I have done with onclick). – Doug Chasman Nov 11 '16 at 12:23
  • 2
    ... Straight from the doc on init: "Use the init event to initialize a component or fire an event after component construction but before rendering." These sections of the doc should help: developer.salesforce.com/docs/atlas.en-us.lightning.meta/… developer.salesforce.com/docs/atlas.en-us.lightning.meta/… Indicates that the initial rendering of the root application or root component has completed. – Doug Chasman Nov 11 '16 at 12:26
  • 2
    I am not sure why LS would have any impact on this though - the issues you are describing should be repro'able independent of LS. The "I can't access elements in init reliably part" is definitely an often discussed challenge - something present long before LS and is also working as designed/documented. I've yet to see a successful use of aura:doneRendering - and never use it anymore myself because of its lack of predictability but it should behave the same way with or without LS and we'll also investigate that too. – Doug Chasman Nov 11 '16 at 12:33
  • 1
    We should also look at even better ways to deal with this - both approaches are actually fighting Lightning for its stewardship of the elements its managing on your behalf. Direct DOM manipulation of elements managed by Lightning has to be done in a very careful and proscribed manner otherwise Lightning is very likely to simply overwrite your changes if/when a rerender cycle is initiated. – Doug Chasman Nov 11 '16 at 15:02

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.