Lightning components - Prevent event bubbling from ui:checkbox to parent div

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.


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.