Elm:将点击事件添加到SVG元素不起作用–这可能吗? [英] Elm: adding click events to SVG elements doesn't work – is this possible?

查看:94
本文介绍了Elm:将点击事件添加到SVG元素不起作用–这可能吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试向Elm中的SVG元素添加on "click"事件,以确定鼠标单击在该元素中的相对位置.

I have been attempting to add an on "click" event to an SVG element in Elm in order to determine the relative position of the mouse click within that element.

下面提供了一个代码示例,您可以尝试在 http://elm-lang.org/try上运行以显示HTML元素上的点击事件如何按预期工作,而SVG元素上却没有.

A code sample is given below that you can try running at http://elm-lang.org/try to show how click events on HTML elements seem to work as expected but not on SVG elements.

在示例中,使用Html.on "click"而不是Html.onClick来从事件中解码位置数据,如

In the sample, Html.on "click" is used rather than Html.onClick to allow the position data to be decoded from the event as explained in this discussion.

阅读文档和源代码后,我希望将on "click"事件添加到SVG元素时,其工作方式与将事件添加到HTML元素的方式相同.但是,完成此操作后,单击SVG元素不会触发该事件,并且不会向更新功能发送任何消息.

After reading the documentation and the source code, I would expect that when the on "click" event is added to an SVG element it would work in the same manner as adding the event to an HTML element. However when this is done, clicking the SVG element does not trigger the event and no message is sent to the update function.

在此示例中,在黑色SVG rect中单击将触发更新功能并更改白色rect的位置,但单击将被忽略.可以通过打开控制台并注意未调用Debug.log来确认. HTML div放在下面,具有相同的click事件,当在div中注册了单击时,白色rect会更改位置.

In this example, clicking within the black SVG rect should trigger the update function and change the position of the white rect but the clicks are ignored. This can be confirmed by opening the console and noting that the Debug.log is not invoked. An HTML div is placed below with an identical click event and when a click is registered inside this div, the white rect changes position.

这是Elm中的预期行为吗,有什么变通办法吗?

Is this intended behaviour in Elm and are there any workarounds?

在stackoverflow

A similar question has been asked on stackoverflow here but this is referring to canvas shapes which, as far as I'm aware, is a completely separate issue (I may be wrong though).

import Html exposing (Html, div)
import Html.App as App
import Html.Attributes
import Html.Events exposing (on)
import Json.Decode as Json exposing (object2, int, at)
import Mouse exposing (Position)
import Svg exposing (svg, rect)
import Svg.Attributes exposing (..)

main =
  App.beginnerProgram
    { model = model
    , view = view
    , update = update
    }

type alias Model =
  Position

type Msg
  = ChangePosition Position

model : Model
model =
  Position 0 0

update : Msg -> Model -> Model
update msg _ =
  case Debug.log "msg" msg of
    ChangePosition position ->
      position

view : Model -> Html Msg
view model =
  div []
    [ svg
        [ width "400"
        , height "100"
        , viewBox "0 0 400 100"
        ]
        [ rect
            [ onClickLocation -- this should work but does nothing
            , width "400"
            , height "100"
            , x "0"
            , y "0"
            , fill "#000"
            , cursor "pointer"
            ]
            []
        , rect
            [ width "50"
            , height "50"
            , x (toString model.x)
            , y "20"
            , fill "#fff"
            ]
            []
        ]
    , div
        [ onClickLocation -- this works
        , Html.Attributes.style
            [ ( "background-color", "white" )
            , ( "border", "2px solid black" )
            , ( "width", "400px" )
            , ( "height", "100px" )
            , ( "position", "absolute" )
            , ( "left", "0px" )
            , ( "top", "150px" )
            , ( "color", "black" )
            , ( "cursor", "pointer" )
            ]
        ]
        [ div [] [ Html.text "Click in here to move x position of white svg square. Relative click coordinates shown below (y coordinate ignored)." ]
        , div [] [ Html.text (toString model) ]
        ]
    ]

onClickLocation : Html.Attribute Msg
onClickLocation =
  on "click"
    (Json.map
      ChangePosition
      (object2
        Position
        (object2 (-)
          (at [ "pageX" ] int)
          (at [ "target", "offsetLeft" ] int)
        )
        (object2 (-)
          (at [ "pageY" ] int)
          (at [ "target", "offsetTop" ] int)
        )
      )
    )

推荐答案

Json解码器不起作用的原因很明显,因为 offsetLeftoffsetTop都不存在 事件对象.

The reason Json decoder did not work is obvious because none of offsetLeft nor offsetTop exist in the event object.

有些混乱,因为这些属性可用 HTML DOM的点击事件,而不是SVG DOM的点击事件. (我的建议 在Elm中实现事件解码器是为了附加临时 浏览器的调试器控制台中的事件处理程序,并研究实际的事件对象.榆树的解码器无声地失败,很难知道为什么解码器 不工作. )

It is somewhat confusing as those properties are available for click event of Html DOM but not for SVG DOM. (My suggestion of implementing event decoders in Elm is to attach temporary event handler in browser's debugger console and study the actual event object. Elm's decoder silently fails and hard to know why the decoder did not work. )

在这里,我实现了另一种方法,您可以使用port来获取 使用javascript的父级位置(不使用任何 社区图书馆).

Here, I implemented an alternate way how you can use port to get parent position using javascript (without using any community libraries).

port module Main exposing (main)

import Html exposing (Html, div)
import Html.App as App
import Html.Attributes
import Html.Events exposing (on)
import Json.Decode as Json exposing (object2, object1, int, at)
import Mouse exposing (Position)
import Svg exposing (svg, rect)
import Svg.Attributes exposing (..)

main : Program Never
main =
  App.program
    { init = (initmodel, getParentPos ())
    , view = view
    , update = update
    , subscriptions = subscriptions
    }

type alias Model =
  { position : Position
  , parentPosition : Position
  }

type Msg
  = ChangePosition Position
  | UpdateParentPosition { top : Int, left : Int }

initmodel : Model
initmodel =
  { position = Position 0 0
  , parentPosition = Position 0 0
  }

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case Debug.log "msg" msg of
    ChangePosition position ->
      let
        relativepos = Position
          ( position.x - model.parentPosition.x )
          ( position.y - model.parentPosition.y )
      in ({ model | position = relativepos } , Cmd.none)
    UpdateParentPosition {top, left} ->
      ({ model | parentPosition = Position top left }, Cmd.none)

port getParentPos : () -> Cmd msg

subscriptions : Model -> Sub Msg
subscriptions model =
  parentPos UpdateParentPosition

port parentPos : ({ top : Int, left : Int } -> msg) -> Sub msg

view : Model -> Html Msg
view model =
  div []
    [ svg
        [ width "400"
        , height "100"
        , viewBox "0 0 400 100"
        , id "parent"
        ]
        [ rect
            [ onClickLocation -- this should work but does nothing
            , width "400"
            , height "100"
            , x "0"
            , y "0"
            , fill "#000"
            , cursor "pointer"
            ]
            []
        , rect
            [ width "50"
            , height "50"
            , x (toString model.position.x)
            , y (toString model.position.y)
            , fill "#fff"
            ]
            []
        ]
    , div
        [ onClickLocation -- this works
        , Html.Attributes.style
            [ ( "background-color", "white" )
            , ( "border", "2px solid black" )
            , ( "width", "400px" )
            , ( "height", "100px" )
            , ( "position", "absolute" )
            , ( "left", "0px" )
            , ( "top", "150px" )
            , ( "color", "black" )
            , ( "cursor", "pointer" )
            ]
        ]
        [ div [] [ Html.text "Click in here to move x position of white svg square. Relative click coordinates shown below (y coordinate ignored)." ]
        , div [] [ Html.text (toString model) ]
        ]
    ]

onClickLocation : Html.Attribute Msg
onClickLocation =
  on "click"
    (Json.map
      ChangePosition
      (object2
        Position
          (at [ "pageX" ] int)
          (at [ "pageY" ] int)
      )
    )

javascript:

javascript:

const app = Elm.Main.fullscreen();

app.ports.getParentPos.subscribe(() => {
  const e = document.querySelector('#parent');
  const rect = e.getBoundingClientRect();
  app.ports.parentPos.send({
    top: Math.round(rect.top),
    left: Math.round(rect.left)
  });
});

这篇关于Elm:将点击事件添加到SVG元素不起作用–这可能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆