7

I'm facing issue with click event bubbling in Lightning.

I have div and inside of it few components including lightning checkbox and <a href> tag:

Component:

<div aura:id="container" class="slds-tree__item" onclick="{!c.onToggleExpand}">
    ...
    <aura:if isTrue="{!v.node.checkboxDisplayed}">
        <ui:inputCheckbox class="inline" aura:id="nodeCheckbox" value="{!v.node.selected}" disabled="{!!v.node.checkboxEnabled}" change="{!c.onCheckboxChange}" click="{!c.onCheckboxClick}"/>
    </aura:if>
    ...
   <a href="javascript:void(0);" onclick="{!c.onNodeClick}" tabindex="-1" role="presentation">{!v.node.name}</a>
</div>

I have handlers in controller for each function, I've simplified them for the sake of brevity:

Controller:

onToggleExpand: function(component, event, helper) {
    console.log('onToggleExpand');
}

onNodeClick: function(component, event, helper) {
    console.log('onNodeClick');
    event.stopPropagation();
}

onCheckboxChange: function(component, event, helper) {
    console.log('onCheckboxChange');
    event.stopPropagation();
}

onCheckboxClick: function(component, event, helper) {
    console.log('onCheckboxClick');
    event.stopPropagation();
    console.log('end');
}

When I click checkbox, in the console I get debugs in following order:

onCheckboxClick
end
onToggleExpand
onCheckboxChange

So, the event.stopPropagation() is totally ignored. What I suppose happens here is that click on child lightning checkbox causes onCheckboxClick handler to execute, but stopping propagation inside this method does not stop standard JS click propagation to parent div. My theory is backed up by fact that when clicking <a href> element, onNodeClick executes and the event is kept from propagating.

Does anyone had similar problems and has workaround for such problems? I could of course change ui:inputCheckbox to SLDS HTML version, but I would prefer to use Lightning components. Maybe something from Lightning that could replace <div>?

----UPDATE:----

I have tried with changing ui:inputCheckbox component to standard SLDS HTML <input type="checkbox"> tag. The code looks like that:

<div aura:id="container" class="slds-tree__item" onclick="{!c.onToggleExpand}">
    ...
    <div class="slds-form-element__control">
        <span class="slds-checkbox">
            <input type="checkbox" id="{!'node-checkbox-' + v.node.id}" checked="{!v.node.selected}" onchange="{!c.onCheckboxChange}" onclick="{!c.onCheckboxClick}" disabled="{!!v.node.checkboxEnabled}"/>
            ...

So standard HTML in both cases. I expect after clicking checkbox first onCheckboxClick is executed and then event is blocked from bubbling up to div element because event.stopPropagation() is used. However, that is not the case. Handlers are executed in following order:

onToggleExpand
onCheckboxClick
onCheckboxChange

WTF Lightning?

3

I still don't know why event.stopPropagation() does not work in this case, but I have solution how to fix the problem.

SLDS checkbox uses well-known CSS technique that hides <input type="checkbox"> element and shows label element that is styled with fancy images to override default checkbox look. Below the code:

<span class="slds-checkbox" onclick="{!c.onCheckboxClick}">
     <input type="checkbox" id="{!'node-checkbox-' + v.node.id}" checked="{!v.node.selected}" onchange="{!c.onCheckboxChange}" disabled="{!!v.node.checkboxEnabled}"/>
     <label class="slds-checkbox__label" for="{!'node-checkbox-' + v.node.id}">
           <span class="slds-checkbox--faux"></span>
     </label>
</span>

The trick is to put your onclick in correct place - if you do it on whole <span class="checkbox">, then no problems with event bubbling will occur. Putting onclick on input will cause click event to "leak" outside checkbox because this isn't the element that uses actually clicks.

--

Btw, I've made additional tests on ui:inputCheckbox and lightning:input type="checkbox". Unfortunately, in this case - that is you want onclick on parent div that is ignoring child checkbox clicks - you cannot use these components, HTML markup is needed.

  • This worked perfectly. In my case, the function in my controller uses component.find('[aura-id-of-checkbox]') to get the value of the checkbox during handling, which still works under this solution. – rael_kid Aug 15 '18 at 12:12
2

The event.stopPropagtion() did work in your code.

You can check the events being propagated using this javascript code:

for(let elem of document.querySelectorAll('*')) {
    elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true);
    elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`));
}

Add this in an external java-script file and include it in your component. You will see tag-by-tag event propagation.

I observed that <ui:inputCheckbox/> is generated dynamically as an <input type="text"/> by Lightning. As a result the component is not registered. So the event propagation is like this

  1. Capture Phase == HTML-->BODY-->DIV ---> then onclick event happens

  2. Bubble Phase == DIV(this is where event.stopPropagtion() is invoked and then the event does not bubble out to BODY and then HTML).

  3. Default Phase == This is where onCheckboxChange() is called.

So you get console log as:

onCheckboxClick
end
onToggleExpand
onCheckboxChange

I tried using standard HTML input tag:

<div aura:id="container" class="slds-tree__item" onclick="{!c.onToggleExpand}">
    <input type="checkbox" id="abc" checked="true" onchange="{!c.onCheckboxChange}" onclick="{!c.onCheckboxClick}">
    <a href="javascript:void(0);" onclick="{!c.onNodeClick}" tabindex="-1" role="presentation">hellooo</a>
</div>

The event.stopPropagation ran perfectly and I got the console log:

onCheckboxClick
end
onCheckboxChange

This is because the input tag was registered properly in the DOM and so its event was handled as expected(i.e. stopped the propagation).

This is where event delegation came into picture. By encapsuling the <ui:inputCheckBox> tag in <Span></Span> tag we delegated the onclick event to a registered component in the DOM (i.e span). Hence the event propagation was handled properly.

(This is also precisely the reason why the anchor tag was able to stop the propagation.)

Code for your refernce:

<div aura:id="container" class="slds-tree__item" onclick="{!c.onToggleExpand}">

    <Span onclick="{!c.onSpanClick}">
        <ui:inputCheckbox class="inline" aura:id="nodeCheckbox" value="true"  change="{!c.onCheckboxChange}" click="{!c.onCheckboxClick}"/>
    </Span>
    <a href="javascript:void(0);" onclick="{!c.onNodeClick}" tabindex="-1" role="presentation">hellooo</a>
</div>

I believe this is the reason why the solution provided by you worked.

I hope by this I am able to clear your doubts regarding your problem.

1

You can use event.getSource to identify check box click and ignore processing that click when div on click handler is invoked

  • 1
    I have already tried that - event.getSource() works only for lightning components events. If event comes from div (which is the case here) you get an error while trying to call event.getSource(). – Wookie88 Apr 22 '17 at 9:47

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.