Elm - 文本区域选择范围消失

2024-01-03

I implemented a <textarea> in Elm such that tabs indent and unindent instead of change focus to another HTML element. Works great except that unindenting sometimes causes the selection to disappear! If I'm selecting the 5th character to the 12th character, I press shift-tab, then it removes 2 tab characters, but it also makes the selection change to a cursor at position 10. The selection range should remain the same..

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

以下是一些屏幕截图来说明问题。紧迫Setup显示这个:

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

不幸的是,按Unindent actually显示以下内容。文本没有缩进,但是选择范围消失了,并且之间只有一个光标gh:


有趣的问题和出色的问题说明!

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

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Elm - 文本区域选择范围消失 的相关文章