1

I'm using Ionic 3 and trying to make a simple 5-star rating element. A cool feature I'd like to implement is sliding/panning to change the rating, similar to how an ion-range works but with no bar or knob, just the stars lighting up as the user slides over them.

I tried using the pan gesture and passing $event as a parameter, and checking the id of the element in the target field of the $event, like

$event.target.id

and assigned each star a corresponding id from 1 through 5.

Testing in the browser it worked just fine, but not in my phone, I checked the whole $event.target object and it is filled with undefined values :( it only changes when the first pan event is fired, all subsequent pans before I release the touch stop working.

Is this a bug? Or maybe is there any workaround for this?

Additional info:

HTML (without style related attributes):

<ion-grid (pan)="ratingChanged($event)">
        <ion-row>
            <ion-col *ngFor="let i of [1,2,3,4,5]">
                <ion-icon [name]="(rating >= i)?'star':'star-outline'" [id]="i"></ion-icon>
            </ion-col>
        </ion-row>
    </ion-grid>

TS:

ratingChanged(ev) {
    if (ev && ev.target && ev.target.id)
        this.rating = +ev.target.id; //id comes as a string
}

EDIT: @Sergey Rudenko's suggestion solved the problem, here's how I implemented it:

HTML:

<ion-grid (pan)="ratingChanged($event)">
    <ion-row>
        <ion-col *ngFor="let i of [1,2,3,4,5]">
            <ion-icon [name]="(rating >= i)?'star':'star-outline'" [id]="'star' + i"></ion-icon>
        </ion-col>
    </ion-row>
</ion-grid>

TS:

ratingChanged($ev) {
    let id: string = document.elementFromPoint($ev.center.x, $ev.center.y).id;
    if (id.match(/star\d/))
        this.rating = +id.match(/star(\d)/)[1];
}

1 Answer 1

1

so there are a few understandings for this:

  1. (pan) is an abstraction event by hammer.js that under the hood uses relevant mousedown, mousemove or touchstart touchmove etc core events for both types of pointers (mouse and touch)

  2. mouse event target is working with the following logic:

    • mouse start's event.target acquires the element it is on top of (farthest in terms of DOM tree), then as you move mouse each mousemove event acquires new target as by design you are "hovering" over with your mouse, hence event.target for mousemove starts to acquire new targets all the time
  3. touch event target has different logic:

    • event.target will be returning "undefined" as event target information with touch events is "buried" in event.touches[0].target for the first touch point touching the screen and so on.
    • another thing is that with touchmove events you sort of "constantly" pressing the screen while moving around, so unlike mousemove which is more like "hover", with touch you sort of "always drag". Hence with touch events - event object will always keep track of the first element that was acquired during touch start.
    • so normally developers use different browser api to acquire target that is underneath touchmove pointer: document.elementFromPoint(event.clientX, event.clientY);

Now joining all this together: basically you need to write code that "detects" what interaction took place (touch or mouse), based on that use proper event target "routing" (use event.target.etc for mouse and event.touches[0].target for touch), then as you move pointer around have implementation that checks elements from pointer coordinates.

I would recommend looking into this article b Josh: https://www.joshmorony.com/building-an-absolute-drag-directive-in-ionic-2/

There I see that you could potentially leverage pan's own event abstraction to get ev.center.x coordinates for both types of pointers, these coordinates then can be used with the elementFromPoint browser api.

Let me know if this is helpful. If you share more of your code I can help with implementation.

1
  • 1
    Wow, I had no idea there was something like the elementFromPoint you suggested. Tried it and now it works perfectly in my phone too. I couldn't find the event.touches, though. But event.center.x and event.center.y worked just fine. I'll edit my post to show the new code, in case someone needs it. Thanks!
    – PiFace
    Commented Aug 9, 2018 at 12:27

Not the answer you're looking for? Browse other questions tagged or ask your own question.