【问题标题】:Elm - textarea selection range disappearing榆树 - 文本区域选择范围消失
【发布时间】:2017-11-23 17:24:31
【问题描述】:

我在 Elm 中实现了<textarea>,这样标签缩进和取消缩进,而不是将焦点转移到另一个 HTML 元素。效果很好,只是取消缩进有时会导致选择消失!如果我选择第 5 个字符到第 12 个字符,我按 shift-tab,然后它会删除 2 个制表符,但它也会使选择变为光标在位置 10。选择范围应保持不变..

我在 Ellie 有一个 SSCCE:https://ellie-app.com/3x2qQdLqpHga1/2

这里有一些截图来说明问题。按设置显示:

然后按Unndent 应该显示以下内容(“def\ng”的选择仍然完好无损):

不幸的是,按下 Unndent 实际上 会显示以下内容。文本没有缩进很好,但选择范围消失了,gh 之间只有一个光标:

【问题讨论】:

  • 我要么误解了问题,要么很难在您的示例中重现您的问题。 tabshift-tab 对我来说离开焦点(选择或未选择文本)。按 setup 然后按 unndent 制表符所有行,然后 unindent 取消后两行制表符。
  • @Tom 按 setup 然后 unndent 不仅应该取消最后 2 行的制表符,而且应该 在“def\ng”上保持选择突出显示。问题是选择消失了。

标签: textarea selection keyboard-events elm


【解决方案1】:

有趣的问题和优秀的问题说明!

问题在于,由于某些原因,当 selectionStart/selectionEnd 的一个属性保持不变时,不会发生重新渲染。尝试将第 42 行的 5 更改为 6。

当您在元素结构中引入强制回流时,它会起作用。见这里:https://ellie-app.com/6Q7h7Lm9XRya1(我将它更新到 0.19 看看是否能解决问题,但它没有)。

请注意,这可能会重新渲染 整个 textarea,因此如果 textarea 是一段巨大的代码,则可能会导致问题。您可以通过在两个相同的文本区域之间交替来解决这个问题,您可以在每次渲染时切换它们的可见性。

module Main exposing (Model, Msg(..), main, update, view)

-- Note: this is Elm 0.19

import Browser
import Browser.Dom exposing (focus)
import Html exposing (Html, button, div, text, textarea)
import Html.Attributes exposing (attribute, class, cols, id, property, rows, style, value)
import Html.Events exposing (onClick)
import Html.Lazy exposing (lazy2)
import Json.Encode as Encode
import Task exposing (attempt)


type alias Model =
    { content : String
    , selectionStart : Int
    , selectionEnd : Int
    -- keep counter of renderings for purposes of randomness in rendering loop
    , renderCounter : Int
    }


main =
    Browser.element
        { init = initModel
        , view = view
        , update = update
        , subscriptions = \s -> Sub.none
        }


initModel : () -> ( Model, Cmd Msg )
initModel flags =
    ( Model "" 0 0 0, Cmd.batch [] )


type Msg
    = Setup
    | Unindent
    | NoOp (Result Browser.Dom.Error ())


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    let
        newRenderCounter =
            model.renderCounter + 1

    in
    case msg of
        Setup ->
            ( { model
                | content = "\tabc\n\tdef\n\tghi"
                , selectionStart = 5
                , selectionEnd = 12
                , renderCounter = newRenderCounter
              }
            , attempt NoOp <| focus "ta"
            )

        Unindent ->
            ( { model
                | content = "\tabc\ndef\nghi"
                , selectionStart = 5
                , selectionEnd = 10
                , renderCounter = newRenderCounter
              }
            , attempt NoOp <| focus "ta"
            )

        NoOp _ ->
            ( model, Cmd.batch [] )


view : Model -> Html Msg
view model =
    div []
        (viewTextarea model model.renderCounter
            ++ [ button [ onClick Setup ] [ text "Setup" ]
               , button [ onClick Unindent ] [ text "Unindent" ]
               ]
        )


viewTextarea : Model -> Int -> List (Html msg)
viewTextarea model counter =
    let

        rerenderForcer =
            div [attribute "style" "display: none;"] []

        ta =
            textarea
                [ id "ta"
                , cols 40
                , rows 20
                , value model.content
                , property "selectionStart" <| Encode.int model.selectionStart
                , property "selectionEnd" <| Encode.int model.selectionEnd
                ]
                []
    in

    -- this is the clue. by alternating this every render, it seems to force Elm to render the textarea anew, fixing the issue. Probably not very performant though. For a performant version, use an identical textarea instead of the div and make sure the two selectionStart/end properties both differ from the previous iteration. Then alternate visibility between the two every iteration.
    if isEven counter then
        [ ta, rerenderForcer ]

    else
        [ rerenderForcer, ta ]


isEven : Int -> Bool
isEven i =
    modBy 2 i == 0

【讨论】:

  • 使用隐藏的&lt;div&gt; 来重排&lt;textarea&gt; 元素的有趣解决方法。感谢您对我的问题描述的称赞。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-21
  • 1970-01-01
  • 1970-01-01
  • 2016-10-10
  • 1970-01-01
  • 1970-01-01
  • 2017-01-09
相关资源
最近更新 更多