# How to implement onClick listener for only when clicked directly?

**URL:** <https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873>\
**Category:** Learn\
**Created:** [September 3, 2018, 8:50pm UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873 "2018-09-03T20:50:31Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Y0hy0h](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/y0hy0h/32/3579_2.png) [@Y0hy0h](https://discourse.elm-lang.org/u/Y0hy0h)\
**Post date:** [September 3, 2018, 8:50pm UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/1 "2018-09-03T20:50:31Z")

</div>

I need to detect a direct click on a list item. The goal is to edit the list items by clicking on them, and exit the edit mode by clicking again. Currently, when I edit an item and click the form, it closes the form. Of course, I only want to close it when I click on some empty space, not when I try to enter something into the form.

So I tried to implement an event listener that only fires when the item is directly clicked. My attempts to implement something similar to `Html.Events.onClick` have failed so far. This is my current code:

```auto
{-| Only fires for clicks exactly on the element.

See <https://javascript.info/bubbling-and-capturing#event-target> for further information.

-}
onDirectClick : Msg -> Attribute Msg
onDirectClick msg =
    let
        decoder =
            Decode.map2 (\current target -> current == target)
                (Decode.field "currentTarget" Decode.string)
                (Decode.field "target" Decode.string)
                |> Decode.andThen
                    (\isDirect ->
                        if isDirect then
                            Decode.succeed msg

                        else
                            Decode.fail "Is not a direct click."
                    )
    in
    on "click" decoder

```

When I click on the list item, nothing happens. I think it’s because `currentTarget` and `target` are not strings, but objects. But I couldn’t find any identifier field in the [documentation](https://developer.mozilla.org/en-US/docs/Web/API/Event/target) that I could use to check whether they are the same.

How can I implement `onDirectClick`?

---

<div class="post-metadata">

**Author:** ![norpan](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/norpan/32/548_2.png) [@norpan](https://discourse.elm-lang.org/u/norpan)\
**Post date:** [September 3, 2018, 9:29pm UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/2 "2018-09-03T21:29:46Z")

</div>

They are JS objects, so you can use `Json.Dedode.value` to just get them as `Value` and then compare them.

---

<div class="post-metadata">

**Author:** ![Y0hy0h](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/y0hy0h/32/3579_2.png) [@Y0hy0h](https://discourse.elm-lang.org/u/Y0hy0h)\
**Post date:** [September 3, 2018, 11:22pm UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/3 "2018-09-03T23:22:49Z")

</div>

Thanks! I don’t know why I didn’t figure that out. 😄

---

<div class="post-metadata">

**Author:** ![Y0hy0h](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/y0hy0h/32/3579_2.png) [@Y0hy0h](https://discourse.elm-lang.org/u/Y0hy0h)\
**Post date:** [September 3, 2018, 11:34pm UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/4 "2018-09-03T23:34:34Z")

</div>

It works for elements that are the same, but when I click on the form (where the click is _not_ direct), I get errors in the console.

```auto
Uncaught Error: Trying to use `(==)` on functions.
There is no way to know if functions are "the same" in the Elm sense.
Read more about this at https://package.elm-lang.org/packages/elm/core/latest/Basics#== which describes why it is this way and what the better version will look like.

```

```auto
Uncaught TypeError: Cannot read property 'click' of undefined

```

```auto
TypeError: y is undefined

```

```auto
TypeError: undefined has no properties

```

All the errors are rooted in the `_Utils_eqHelp` function. I believe that this means that Elm cannot compare two differently structured objects as easily as I assumed.

I will investigate this later.

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [September 4, 2018, 12:49am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/5 "2018-09-04T00:49:51Z")

</div>

I think the simplest thing is to [stop propagation](https://package.elm-lang.org/packages/elm/html/latest/Html-Events#stopPropagationOn). Like this, no indirect click can fire if a direct click fired first. You can simply send the message defined for when the click happens.  
Well It can be indirect if your list item has a dom sub hierarchy but I don’t think that’s what your intention really is right?

---

<div class="post-metadata">

**Author:** ![Y0hy0h](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/y0hy0h/32/3579_2.png) [@Y0hy0h](https://discourse.elm-lang.org/u/Y0hy0h)\
**Post date:** [September 4, 2018, 7:52am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/6 "2018-09-04T07:52:04Z")

</div>

I’ve created [an Elli](https://ellie-app.com/3fhmCGj4czWa1) that demonstrates the minimal setup for my struggle.

When you try to enter something in edit mode, the click causes the edit mode to close. And I don’t want to attach `stopPropagationOn` listeners, because I have multiple elements (accept & reset buttons) that would need that attached, too. So I think I really am looking for a listener on the container `div` that only fires when directly clicked.

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [September 4, 2018, 8:31am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/7 "2018-09-04T08:31:34Z")

</div>

I’m not to convinced about UX of having text switch from editable to not editable on click, instead of by another visual indicator, but for the sake of the exercise:

Here is a working example with what I meant by using stop propagation: [https://ellie-app.com/3fhTC72Shwba1](https://ellie-app.com/3fhTC72Shwba1). With a helper function it’s really not such a hassle. (The div doesn’t occupy the whole page so click on the right of the text to trigger the div onclick)

I’ll see if I can have another version with current target checks.

---

<div class="post-metadata">

**Author:** ![Y0hy0h](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/y0hy0h/32/3579_2.png) [@Y0hy0h](https://discourse.elm-lang.org/u/Y0hy0h)\
**Post date:** [September 4, 2018, 8:59am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/8 "2018-09-04T08:59:51Z")

</div>

> `form [] [ input [ stopOn "click" noMsg, ...`

I would prefer to not “blacklist” every element that is in front of the `div`. If we manage to get a `onDirectClick` listener, that is the cleaner approach from my point of view.

An aside on my UX reasoning: I have a list of todos. The app is supposed to be heavily used on mobile. There, tapping a list item to expand and edit it (edit text, delete) makes sense. That is what I’m trying to implement.

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [September 4, 2018, 9:06am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/9 "2018-09-04T09:06:52Z")

</div>

And here is an example that uses the id of the element to check if it is the direct target: [https://ellie-app.com/3fjt7546Pkga1](https://ellie-app.com/3fjt7546Pkga1) (had to add things below because the form element is taking the full width, maybe it can be shrinked but I’m not a css ninja so no idea ^^).

---

<div class="post-metadata">

**Author:** ![Y0hy0h](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/y0hy0h/32/3579_2.png) [@Y0hy0h](https://discourse.elm-lang.org/u/Y0hy0h)\
**Post date:** [September 4, 2018, 10:48am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/10 "2018-09-04T10:48:17Z")

</div>

Your idea is a good workaround! I modified it slightly, so that only the `div` needs an `id` and fixed the issue with the sizing: [https://ellie-app.com/3fkYKTjpPkMa1](https://ellie-app.com/3fkYKTjpPkMa1)

While this idea works quite well around the problem, I would still prefer the cleaner approach that consists of simply the `onDirectClick` listener. 😉 But thanks for that workaround!

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [September 4, 2018, 11:08am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/11 "2018-09-04T11:08:19Z")

</div>

Yes the multiples ids were not needed ahah that was just for debugging the output in console ^^. (the `NoMsg` isn’t needed as well btw, leftover of previous example).

---- Edit  
You can slightly improve this by comparing the id of `target` and `currentTarget` instead of passing one id as the argument of the decoder. But this still needs you to set an id, otherwise you have `"" == ""` which is always true.

---- End edit

I think you won’t find a pure elm solution for your problem for the simple reason that elm cannot compare `Value` elements (cf [equality doc](https://package.elm-lang.org/packages/elm-lang/core/latest/Basics#(==))). So the only way of identifying that two dom elements are the same is through a unique identifier accessible at the decoding phase. I’ve quickly search to see if there was one such “automatically” available in browser nodes and didn’t find so I settled to set the `id` property myself.

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [September 4, 2018, 11:17am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/12 "2018-09-04T11:17:35Z")

</div>

PS, as you may have learned [in the guide](https://guide.elm-lang.org/optimization/keyed.html), using `keyed` views for things like lists is quite important to avoid some issues that may appear due to how the virtual dom works. Knowing this, it means that you will already have identifiers available so it plays nicely with this technique using the `id` to detect that current target is target.

---

<div class="post-metadata">

**Author:** ![Y0hy0h](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/y0hy0h/32/3579_2.png) [@Y0hy0h](https://discourse.elm-lang.org/u/Y0hy0h)\
**Post date:** [September 4, 2018, 11:24am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/13 "2018-09-04T11:24:09Z")

</div>

Thanks for the link to the equality doc. That sounds like the best place to implement this listener would be in `elm/html`. I will see if I get around to making a feature proposal.

Thanks for your support! I learned a lot of things and have a decent workaround! 🙂

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [September 14, 2018, 11:26am UTC](https://discourse.elm-lang.org/t/how-to-implement-onclick-listener-for-only-when-clicked-directly/1873/14 "2018-09-14T11:26:02Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
