6

Szenario
We want to refactor a dynamic created list by using only one click event listener on the surrounding <ul>-tag rather having one on each <li>-tag as recommended by Christophe Coenraets performance optimizations-blog.

Setup reproducible test environment
Install the Mutual Fund Explorer Demo application as described. Now switch the Bundle Version Settings to API version v40 (could be done in Developer Console, but must be done on each component separately). Switch to Lightning Experience / DreamInvest App and select the tab Fund Explorer. Now hover over the star-rating component from one of the displayed Funds: enter image description here.

Move your mouse along the stars - you will see that the popover is not following your mouse (only over the stars)

Faulty behavior
This is where the code breaks (FundTileListController.js - onMouseMove L.66):

onMouseMove: function(component, event, helper) {
    if (event.target === event.currentTarget) return;
    var el = event.target;
    while (el && (!el.dataset || !el.dataset.id)) {
        el = el.parentElement;
    }
    if (el) {
        var funds = component.get("v.funds");
        component.find("popup").showPopup(funds[el.dataset.id], event.clientX + 20, el.offsetTop - 46);
    }
},

With API version 40 the while-loop exits without referencing the correct <li>-tag which contains the data-id tag.

When does it break exactly?
Now the question comes up: why is it only faulty when moving over the stars and not over the rest of the components?
We found out, it only breaks with Lightning Components which are using a different markup when getting compiled - meaning when you define <div><span>...</div></span> this is exactly what you get in the rendered source, but when you use one of the framework components like <lightning:icon> this is compiled into a different markup like <div><span><svg><use /></svg></span></div>. We also could reproduce the behavior for <ui:OutputText> and <lightning:layout>

What is the impact beyond the demo?
We have a reusable list component which does not know exactly what class is used for visualization. So if we use a sth. like <lightning:icon> we are not able to select the list item anymore.

What could be the reason for it?
I guess that the event propagation is handled different through the Locker Service updates in Summer17 which shouldn't be the case. But please: any thoughts on this are welcome.

Final Question
Can anybody confirm that this is a bug in the framework which will be fixed? How can I work around by following performance best practices only using one click handler on a <ul>-tag and getting data for a clicked <li>-tag by the event object without trapping into this issue?

  • I put my money that this is a LockerService restriction. – ransommule Jun 12 '17 at 16:02
  • In the Webinar "Enhanced Security with Locker-Service" from JF Paradis a couple of minutes ago they had a slide that showed that parentElement is different for API v40 especially when you try to access other components DOM (this would explain why it only breaks when using Base Lightning Components and not plain HTML) - but I would be glad if this could be verified by Salesforce. Also: what would be a proper workaround? – Christian Menzinger Jun 13 '17 at 16:19
1

Christian, thanks for the detailed information. This is not only helping us to understand the issue, but I'm sure the whole community will benefit from your remarks.

What's going on?

As I undestand it, the behavior you are witnessing is working by design. And it's not only because of LockerService.

You see, in a component-based architecture, what's inside of a component should be opaque to others. The new web component standards defines a "shadow DOM" that is not accessible by others: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM

Why is this happening?

Behind the shadow DOM lies the old principle of encapsulation: units should not leak their internal implementation to others, and others should only access the interface.

Why is this happening with LockerService?

LockerService allows us to add to Aura/Lightning something we wanted to enforce since the beginning: encapsulation. And we will most likely, and like all major frameworks, use the native shadow DOM in the future. Therefore, if it's not LockerService, something else will enforce that behavior.

Now, with LockerService, the emulation of the shadow DOM isn't perfect, but it's enough for security purposes, and it's more compatible with current web development practices than the real shadow DOM.

By that I mean that you can break encapsulation and peek into any component, as long as it's in your own namespace. In the example above, you can't loop inside components like <lightning:icon> and <ui:OutputText> because they belong to other namespaces, lightning and ui respectively.

What can you do?

It looks like the library is trying to "bubble" manually using javascript to find the closest parent that has the right attributes.

When hovering on a component that you don't own (from a different namespace) the process should start at that container, so when you hover on your <lightning:icon>, there is something to bubble from.

If that's not what is happening now, then I'm happy to look into this furthermore with you, but I guess we need a little more info, and maybe a simple repro if that is possible.

  • Thank you very much JF for that detailed explanation. I understand now WHY the code breaks. The question is HOW I can follow performance best practices on dynamic lists as Christophe Coenraets proposed. Here is a super simple mini example which demonstrates the event handling - a click on the icon is handled different as a click on the text (gist.github.com/ChristianMenzinger/…). It works like expected with API v39 and fails with API v40. Do you have any idea how to work around if that behavior is per design? – Christian Menzinger Jun 16 '17 at 7:54
  • Thanks, that's super useful. I will look into it. I don't know why LockerService would break your event aggregator pattern. What I suspect is that LockerService needs to move the eventTarget up the chain to the fist DOM element. – JF Paradis Jun 16 '17 at 16:57
  • Here is an update: the event.target should probably not return elements part of the "shadow" DOM as it is now, but climb up to the last opaque elements were your parent element can pick up. There no known workaround at this point. – JF Paradis Jun 17 '17 at 4:25
  • Thank you for investigating this issue. Did I understand it right that event propagation should start at the first "visible" element within my scope (which then would work as expected) but is not the case right now? Is there a chance to get this fixed? – Christian Menzinger Jun 19 '17 at 7:14
  • 2
    Hi Christian, you are right. The behavior we emulate in LockerService needs to be changed. I've logged a bug for it. I don't have an ETA yet. – JF Paradis Jun 20 '17 at 17:11

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.