3

According to Salesforce documentation,

The afterRender() method is called to enable you to interact with the DOM tree after the framework’s rendering service has inserted DOM elements

I have a Lightning component and I want to add some class to an element after the component has completed its load. But this doesn't work, the class doesn't get added to the element. Here is my code.

({
    afterRender: function(cmp, helper) {
        this.superAfterRender();
        var lst_officeElements = document.getElementsByClassName('myMenuItem');
        lst_officeElements[0].className += " slds-active";
    }
})

When I use a timer though, with JavaScript function window.setTimeout() with 2 seconds delay, it works.

Can anyone help me with this? Is my understanding of the afterRender() method correct? I'm expecting this method to do stuff on the DOM ounce my component has completely loaded its content (DOM), kind of an equivalent of the jQuery function .ready().

Here is my Markup:

<aura:component controller="OfficeCtrl" implements="flexipage:availableForAllPageTypes" access="global"> 
    <aura:handler name="init" action="{!c.doInit}" value="{!this}" />
    <aura:registerEvent name="OfficeEvent" type="c:OfficeEvent" />
    <aura:attribute name="offices" type="Office__c[]" />
    <aura:attribute name="officesMap" type="Map" />
    <div class="slds-tabs--default">
        <ul class="slds-tabs--default__nav" role="tablist">
            <aura:iteration items="{!v.offices}" var="myOffice">
                <li class="myMenuItem slds-tabs--default__item slds-text-title--caps" title="{!myOffice.Name}" role="presentation" id="{!myOffice.Id}" onclick="{!c.selectOffice}">
                    <a class="slds-tabs--default__link" role="tab" aria-selected="false">
                        {!myOffice.Name}
                    </a>
                </li>
            </aura:iteration>
        </ul>
    </div>
</aura:component>

Here is the rest of the code of my component (it is not really related to the problem I'm facing though). Here is the controller:

({
    doInit : function(cmp, event, helper) {
        let getOfficesAction = cmp.get("c.getOffices");
        getOfficesAction.setCallback(this, function(response) {
            let state = response.getState();
            if (cmp.isValid() && state === "SUCCESS") {
                let lst_offices = response.getReturnValue();
                if (lst_offices.length == 0) {
                    return;
                }

                let map_offices = {};
                let size = lst_offices.length;
                for (let i = 0; i < size; i++) {
                    map_offices[lst_offices[i].Id] = lst_offices[i];
                }

                cmp.set("v.offices", lst_offices); 
                cmp.set("v.officesMap", map_offices); 

                let officeEvent = $A.get("e.c:OfficeEvent");
                officeEvent.setParams({"office": lst_offices[0]});
                officeEvent.fire();
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        $A.enqueueAction(getOfficesAction);
    },

    selectOffice: function(cmp, event, helper) {
        let map_offices = cmp.get("v.officesMap");
        let lst_offices = cmp.get("v.offices");
        let currentOfficeId = event.currentTarget.getAttribute("id");
        helper.selectOfficeHlp(lst_offices, map_offices, currentOfficeId);
    }
})

And here is the helper:

({
    selectOfficeHlp: function(p_offices, p_officesMap, p_currentOfficeId) {
        if (p_offices.length == 0) {
            return;
        }

        p_offices.map(function(myOffice) {
            document.getElementById(myOffice.Id).className = "myMenuItem slds-tabs--default__item slds-text-title--caps";
        });

        $A.util.addClass(document.getElementById(p_currentOfficeId), 'slds-active');
        this.fireOfficeEvent(p_officesMap[p_currentOfficeId]);
    },

    fireOfficeEvent: function(p_office) {
        let officeEvent = $A.get("e.c:OfficeEvent");
        officeEvent.setParams({"office": p_office});
        officeEvent.fire();
    }
})

Thanks a lot

4
  • 1
    Can you post the rest of your code as well? Dec 2, 2016 at 23:39
  • OK, can you publish the rest of your code -- e.g. controller? Dec 3, 2016 at 0:49
  • It is not really related to the problem I'm facing, but I will update my question. Dec 3, 2016 at 0:52
  • i am also looking for the solution. did you find one. if not can you please give your code with time delay.
    – ajay rawat
    Apr 6, 2019 at 3:03

2 Answers 2

1

Second try here...

For adding class Names I'd suggest you use $A.util.addClass(), the component has a chance to rerender a lot and it will clobber your changes if you don't set it on the component as well

$A.util.addClass(cmp, "slds-active");

You could also try using getElement() on the component

var element = cmp.getElement().classList.add("slds-active");
6
  • Thanks for the suggestion. The ret variable returns undefined (I displayed its content with console.log) Dec 2, 2016 at 23:54
  • 1
    You are right, thats only for superRender and superRerender.
    – Kris Gray
    Dec 2, 2016 at 23:55
  • With the first one, I got the same result as before. With the second one, I got an Aura error: Cannot read property 'classList' of undefined Dec 3, 2016 at 0:07
  • How is the element getting output? Whats the markup for your cmp file?
    – Kris Gray
    Dec 3, 2016 at 0:16
  • I updated the question to include the markup. It is a very simple component. I just want to add slds-active class after component load, so the first element of my list is automatically underlined in UI Dec 3, 2016 at 0:23
0

OK, afterRender only fires after the render function is called -- when your component is first rendered. It fires once. Then your object will load and the afterRender will not fire again. Rerender will fire, so try putting this into a rerender instead.

Or if you want put it in a helper and call it from both renderers to handle the situation in which the object is loaded into your component by a parent in addition to being loaded from the server async. That would cover all cases.

Also, please do not search the whole document. Scope your query down to the element in your component. If someone uses the same classname in another component, you will be modifying their style. Once Locker is out everywhere, this type of encapsulation will be done for you, but in the meantime, please scope down your queries.

4
  • Thanks. I put it in both afterRender() and rerender(). It didn't work. Dec 3, 2016 at 1:35
  • Yeah you're right, good point (for the styling scoping). But I'm still not able to do what I wanted to do: changing component style after the component has completly finished rendering the Dom Dec 3, 2016 at 2:20
  • What happens when you put some debugger statements into your custom rerender? E.g. is your DOM element visible to the rerender? Dec 3, 2016 at 2:55
  • I tried that. The element is undefined. It means that at that particular time, the element is not rendered yet. I tried the output on the afterRender() and the rerender() methods Dec 3, 2016 at 4:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .