11

I'm trying to move a popover created with CSS, relative to the cursor position. I believe I could do this if I had access to the full DOM, but as far as I understand the LockerService prevents this. The code below shows my current approach, which isn't working.

Component markup:

<div aura:id="popover" class="popover">
    <p class="popover-text">
        Some text
    </p>
</div>

Javascript:

var popovers = cmp.find("popover");

window.onmousemove = function (e) {

    //this isn't the DOM element, and .getElement() no longer works.
    //var popover = popovers[0];

    var x = e.clientX;
    var y = e.clientY;
    for (var i = 0; i < popovers.length; i++) {
        var popover = popovers[i];
        popover.style.top = (y + 20) + 'px';
        popover.style.left = (x + 20) + 'px';
    }
};

The component is has position: absolute and other properties set by CSS not shown. It displays correctly, except I would like it to move with the cursor and not be stationary. Is this currently possible in Lightning?

EDIT: In this case I've realised the question can be summed up as "is it possible to set an element's style via javascript without going via a CSS class?." Unless there's still a better way in Lightning.

3
  • Is this question really more than a pure javascript question? Perhaps it belongs on Stack Overflow?
    – Adrian Larson
    Dec 1 '16 at 22:27
  • 6
    The problem isn't the javascript, it's the fact that in Lightning your javascript doesn't have full access to the DOM. The given code would work (if tweaked back to normal html/javascript) if not in Lightning. The question is how to make this simple use of javascript work in Lightning, or whether there is an alternative method that works in Lightning.
    – nicstella
    Dec 1 '16 at 22:35
  • In the world of Locker Service in Lightning, so many trivial JavaScript techniques come to ruin and made more ardruous. Viva LockerService.... =/
    – Doug Ayers
    Dec 2 '16 at 6:52
3

You can do this native in Lightning with locker service, but the way you do it is kind of tricky.

Here's the solution I came up with that works. Explanation at the end.

Application.app

<aura:application >
    <aura:attribute name="popovers" type="List" default="['1','2','3','4','5']" />
    <div class="root">
        <aura:iteration items="{!v.popovers}" var="popover" indexVar="index">
            <span class="ta" onmousemove="{!c.updatelocation}" data-index="{!index}">
                This is the text of {!popover}.
            </span>
            <span aura:id="text" class="ta-text">
                This is the tooltip for the {!popover} area.
            </span>
        </aura:iteration>
    </div>
</aura:application>

ApplicationController.js

({
    updatelocation: function(component, event, helper) {
        var index = event.currentTarget.dataset.index;
        var text = index > -1? component.find("text")[index].getElement(): null;
        if(text) {
            text.style.left = (event.clientX+20)+'px';
            text.style.top = (event.clientY-20)+'px';
        }
    }
})

Application.css

.THIS.root {
    padding: 2em;
}
.THIS .ta {
    position: relative;
}
.THIS .ta + .ta-text {
    display: none;
}
.THIS .ta:hover + .ta-text {
    display: inherit;
    position: fixed;
    background: #ff8;
    border: 1px solid red;
    width: 25rel;
    height: 10rel;
    z-index: 9999;
}

In order to avoid traversing the DOM, we use component.find to find our inner HTML elements, then read the index value off of the mouseover area, and if we find popover text, we set its position.

The CSS itself sets up the elements to disappear when their parent is no longer being hovered, so the only JavaScript we need to do is setting the x/y position.

Note that because of z-index stacking rules, the elements are positioned in a way (siblings instead of nested). This prevents the tooltip from appearing under popup areas rendered nearby.

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.