shulmj_
@shulmj_

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

高さを自動調節するtextareaが、1行分余計に伸びてしまう

解決したいこと

高さを自動調節するtextareaを実装しようとしています。
動作自体は上手く言ったのですが、末尾の行が空欄の時にのみ、高さが1行分余計に増えてしまいます。

(Chromeの拡張機能を用いて、フロント側から既存のウェブサイトのtextareaの挙動を変更しようとしています)

① textareaに適当な文章を入力
screen1.png

② 改行すると、高さが1行分余計に伸びる
screen2.png

③ 末尾の行に文字を入力すると、適切な長さに戻る
screen3.png

環境

  • ブラウザ:Google Chrome 126.0.6439.0(Official Build)dev
  • jQuery: 3.7.1

該当するソースコード

HTML
<textarea placeholder="本文を入力..."></textarea>
JavaScript
$("textarea").each(function(){
    var elm = $(this)
    const ch = elm.height()
    elm.on("input", function(){
        elm.height(ch)
        const sh = elm.get(0).scrollHeight
        elm.height(sh)
    })
})
CSS
textarea {
	display: inline-block;
    box-sizing: border-box;
	width: 100%;
    min-height: 20px;
    border: 2px solid rgba(68, 68, 68, .1);
	border-radius: 0;
    padding: .25em .5em;
    resize: none;
    overflow: auto;
    overflow-y: hidden;
}

自分で試したこと

上記問題は、Google Chrome 126.0.6439.0(Official Build)devで確認しました。
FireFox 125.0.1では正常に動作しました。

解決方法をご存じの方がいらっしゃいましたら、ご教示いただければと思います。

追記

Dev版が怪しいとのコメントを頂き、正式版でもテストしてみましたが同様の挙動を再現しました。
バージョン: 124.0.6367.63(Official Build) (64 ビット)

開発者向けモードの「計算済み」タブ欄
* {
    appearance: auto;
    background-color rgb(255, 255,: 255);
    border-bottom-color rgb(118, 118,: 118);
    border-bottom-style: solid;
    border-bottom-width: 0.8px;
    border-image-outset: 0;
    border-image-repeat: stretch;
    border-image-slice: 100%;
    border-image-source: none;
    border-image-width: 1;
    border-left-color rgb(118, 118,: 118);
    border-left-style: solid;
    border-left-width: 0.8px;
    border-right-color rgb(118, 118,: 118);
    border-right-style: solid;
    border-right-width: 0.8px;
    border-top-color rgb(118, 118,: 118);
    border-top-style: solid;
    border-top-width: 0.8px;
    box-sizing: border-box;
    color rgb(68, 68,: 68);
    column-count: auto;
    cursor: text;
    direction: ltr;
    display: inline-block;
    font-family メイリオ, Meiryo, "Hiragino Kaku Gothic ProN", "ヒラギノ角ゴ ProN W3",: sans-serif;
    font-feature-settings: normal;
    font-kerning: normal;
    font-optical-sizing: auto;
    font-size: 16px;
    font-stretch: 100%;
    font-style: normal;
    font-variant-alternates: normal;
    font-variant-caps: normal;
    font-variant-east-asian: normal;
    font-variant-ligatures: normal;
    font-variant-numeric: normal;
    font-variant-position: normal;
    font-variation-settings: normal;
    font-weight: 400;
    height: 67.6px;
    letter-spacing: normal;
    line-height: 28.8px;
    margin-bottom: 0px;
    margin-left: 0px;
    margin-right: 0px;
    margin-top: 0px;
    overflow-wrap: break-word;
    overflow-x: auto;
    overflow-y: auto;
    padding-bottom: 2px;
    padding-left: 2px;
    padding-right: 2px;
    padding-top: 2px;
    resize: both;
    text-align: start;
    text-indent: 0px;
    text-rendering: auto;
    text-shadow: none;
    text-size-adjust: 100%;
    text-transform: none;
    text-wrap: wrap;
    unicode-bidi: bidi-override;
    white-space-collapse: preserve;
    width: 197.6px;
    word-spacing: 0px;
    -webkit-font-smoothing: subpixel-antialiased;
    -webkit-rtl-ordering: logical;
    -webkit-border-image: none;
}
開発者向けモードの「計算済み」タブ欄(すべて表示)
* {
    accent-color: auto;
    align-content: normal;
    align-items: normal;
    align-self: auto;
    alignment-baseline: auto;
    all
    anchor-name: none;
    animation-composition: replace;
    animation-delay: 0s;
    animation-direction: normal;
    animation-duration: 0s;
    animation-fill-mode: none;
    animation-iteration-count: 1;
    animation-name: none;
    animation-play-state: running;
    animation-range-end: normal;
    animation-range-start: normal;
    animation-timeline: auto;
    animation-timing-function: ease;
    app-region: none;
    appearance: auto;
    aspect-ratio: auto;
    backdrop-filter: none;
    backface-visibility: visible;
    background-attachment: scroll;
    background-blend-mode: normal;
    background-clip: border-box;
    background-color rgb(255, 255,: 255);
    background-image: none;
    background-origin: padding-box;
    background-position-x: 0%;
    background-position-y: 0%;
    background-repeat: repeat;
    background-size: auto;
    baseline-shift: 0px;
    baseline-source: auto;
    block-size: 67.6px;
    border-block-end-color rgb(118, 118,: 118);
    border-block-end-style: solid;
    border-block-end-width: 0.8px;
    border-block-start-color rgb(118, 118,: 118);
    border-block-start-style: solid;
    border-block-start-width: 0.8px;
    border-bottom-color rgb(118, 118,: 118);
    border-bottom-left-radius: 0px;
    border-bottom-right-radius: 0px;
    border-bottom-style: solid;
    border-bottom-width: 0.8px;
    border-collapse: separate;
    border-end-end-radius: 0px;
    border-end-start-radius: 0px;
    border-image-outset: 0;
    border-image-repeat: stretch;
    border-image-slice: 100%;
    border-image-source: none;
    border-image-width: 1;
    border-inline-end-color rgb(118, 118,: 118);
    border-inline-end-style: solid;
    border-inline-end-width: 0.8px;
    border-inline-start-color rgb(118, 118,: 118);
    border-inline-start-style: solid;
    border-inline-start-width: 0.8px;
    border-left-color rgb(118, 118,: 118);
    border-left-style: solid;
    border-left-width: 0.8px;
    border-right-color rgb(118, 118,: 118);
    border-right-style: solid;
    border-right-width: 0.8px;
    border-start-end-radius: 0px;
    border-start-start-radius: 0px;
    border-top-color rgb(118, 118,: 118);
    border-top-left-radius: 0px;
    border-top-right-radius: 0px;
    border-top-style: solid;
    border-top-width: 0.8px;
    bottom: auto;
    box-shadow: none;
    box-sizing: border-box;
    break-after: auto;
    break-before: auto;
    break-inside: auto;
    buffered-rendering: auto;
    caption-side: top;
    caret-color rgb(68, 68,: 68);
    clear: none;
    clip: auto;
    clip-path: none;
    clip-rule: nonzero;
    color rgb(68, 68,: 68);
    color-interpolation: srgb;
    color-interpolation-filters: linearrgb;
    color-rendering: auto;
    color-scheme: normal;
    column-count: auto;
    column-fill: balance;
    column-gap: normal;
    column-rule-color rgb(68, 68,: 68);
    column-rule-style: none;
    column-rule-width: 0px;
    column-span: none;
    column-width: auto;
    contain: none;
    contain-intrinsic-block-size: none;
    contain-intrinsic-height: none;
    contain-intrinsic-inline-size: none;
    contain-intrinsic-width: none;
    container-name: none;
    container-type: normal;
    content: normal;
    content-visibility: visible;
    counter-increment: none;
    counter-reset: none;
    counter-set: none;
    cursor: text;
    cx: 0px;
    cy: 0px;
    d: none;
    direction: ltr;
    display: inline-block;
    dominant-baseline: auto;
    empty-cells: show;
    field-sizing: fixed;
    fill rgb(0, 0,: 0);
    fill-opacity: 1;
    fill-rule: nonzero;
    filter: none;
    flex-basis: auto;
    flex-direction: row;
    flex-grow: 0;
    flex-shrink: 1;
    flex-wrap: nowrap;
    float: none;
    flood-color rgb(0, 0,: 0);
    flood-opacity: 1;
    font-family メイリオ, Meiryo, "Hiragino Kaku Gothic ProN", "ヒラギノ角ゴ ProN W3",: sans-serif;
    font-feature-settings: normal;
    font-kerning: normal;
    font-optical-sizing: auto;
    font-palette: normal;
    font-size: 16px;
    font-stretch: 100%;
    font-style: normal;
    font-synthesis-small-caps: auto;
    font-synthesis-style: auto;
    font-synthesis-weight: auto;
    font-variant-alternates: normal;
    font-variant-caps: normal;
    font-variant-east-asian: normal;
    font-variant-ligatures: normal;
    font-variant-numeric: normal;
    font-variant-position: normal;
    font-variation-settings: normal;
    font-weight: 400;
    forced-color-adjust: auto;
    grid-auto-columns: auto;
    grid-auto-flow: row;
    grid-auto-rows: auto;
    grid-column-end: auto;
    grid-column-start: auto;
    grid-row-end: auto;
    grid-row-start: auto;
    grid-template-areas: none;
    grid-template-columns: none;
    grid-template-rows: none;
    height: 67.6px;
    hyphenate-character: auto;
    hyphenate-limit-chars: auto;
    hyphens: manual;
    image-orientation: from-image;
    image-rendering: auto;
    initial-letter: normal;
    inline-size: 197.6px;
    inset-area: none;
    inset-block-end: auto;
    inset-block-start: auto;
    inset-inline-end: auto;
    inset-inline-start: auto;
    isolation: auto;
    justify-content: normal;
    justify-items: normal;
    justify-self: auto;
    left: auto;
    letter-spacing: normal;
    lighting-color rgb(255, 255,: 255);
    line-break: auto;
    line-height: 28.8px;
    list-style-image: none;
    list-style-position: outside;
    list-style-type: disc;
    margin-block-end: 0px;
    margin-block-start: 0px;
    margin-bottom: 0px;
    margin-inline-end: 0px;
    margin-inline-start: 0px;
    margin-left: 0px;
    margin-right: 0px;
    margin-top: 0px;
    marker-end: none;
    marker-mid: none;
    marker-start: none;
    mask-clip: border-box;
    mask-composite: add;
    mask-image: none;
    mask-mode: match-source;
    mask-origin: border-box;
    mask-repeat: repeat;
    mask-size: auto;
    mask-type: luminance;
    math-depth: 0;
    math-shift: normal;
    math-style: normal;
    max-block-size: none;
    max-height: none;
    max-inline-size: none;
    max-width: none;
    min-block-size: 0px;
    min-height: 0px;
    min-inline-size: 0px;
    min-width: 0px;
    mix-blend-mode: normal;
    object-fit: fill;
    object-position 50%: 50%;
    object-view-box: none;
    offset-anchor: auto;
    offset-distance: 0px;
    offset-path: none;
    offset-position: normal;
    offset-rotate auto: 0deg;
    opacity: 1;
    order: 0;
    orphans: 2;
    outline-color rgb(68, 68,: 68);
    outline-offset: 0px;
    outline-style: none;
    outline-width: 0px;
    overflow-anchor: auto;
    overflow-clip-margin: 0px;
    overflow-wrap: break-word;
    overflow-x: auto;
    overflow-y: auto;
    overlay: none;
    overscroll-behavior-block: auto;
    overscroll-behavior-inline: auto;
    overscroll-behavior-x: auto;
    overscroll-behavior-y: auto;
    padding-block-end: 2px;
    padding-block-start: 2px;
    padding-bottom: 2px;
    padding-inline-end: 2px;
    padding-inline-start: 2px;
    padding-left: 2px;
    padding-right: 2px;
    padding-top: 2px;
    page: auto;
    page-orientation
    paint-order: normal;
    perspective: none;
    perspective-origin 98.8px: 33.8px;
    pointer-events: auto;
    position: static;
    position-anchor: implicit;
    position-try-options: none;
    position-try-order: normal;
    position-visibility: always;
    quotes: auto;
    r: 0px;
    resize: both;
    right: auto;
    rotate: none;
    row-gap: normal;
    ruby-position: over;
    rx: auto;
    ry: auto;
    scale: none;
    scroll-behavior: auto;
    scroll-margin-block-end: 0px;
    scroll-margin-block-start: 0px;
    scroll-margin-bottom: 0px;
    scroll-margin-inline-end: 0px;
    scroll-margin-inline-start: 0px;
    scroll-margin-left: 0px;
    scroll-margin-right: 0px;
    scroll-margin-top: 0px;
    scroll-padding-block-end: auto;
    scroll-padding-block-start: auto;
    scroll-padding-bottom: auto;
    scroll-padding-inline-end: auto;
    scroll-padding-inline-start: auto;
    scroll-padding-left: auto;
    scroll-padding-right: auto;
    scroll-padding-top: auto;
    scroll-snap-align: none;
    scroll-snap-stop: normal;
    scroll-snap-type: none;
    scroll-timeline-axis: block;
    scroll-timeline-name: none;
    scrollbar-color: auto;
    scrollbar-gutter: auto;
    scrollbar-width: auto;
    shape-image-threshold: 0;
    shape-margin: 0px;
    shape-outside: none;
    shape-rendering: auto;
    size
    speak: normal;
    stop-color rgb(0, 0,: 0);
    stop-opacity: 1;
    stroke: none;
    stroke-dasharray: none;
    stroke-dashoffset: 0px;
    stroke-linecap: butt;
    stroke-linejoin: miter;
    stroke-miterlimit: 4;
    stroke-opacity: 1;
    stroke-width: 1px;
    tab-size: 8;
    table-layout: auto;
    text-align: start;
    text-align-last: auto;
    text-anchor: start;
    text-combine-upright: none;
    text-decoration-color rgb(68, 68,: 68);
    text-decoration-line: none;
    text-decoration-skip-ink: auto;
    text-decoration-style: solid;
    text-decoration-thickness: auto;
    text-emphasis-color rgb(68, 68,: 68);
    text-emphasis-position: over;
    text-emphasis-style: none;
    text-indent: 0px;
    text-orientation: mixed;
    text-overflow: clip;
    text-rendering: auto;
    text-shadow: none;
    text-size-adjust: 100%;
    text-spacing-trim: normal;
    text-transform: none;
    text-underline-offset: auto;
    text-underline-position: auto;
    text-wrap: wrap;
    timeline-scope: none;
    top: auto;
    touch-action: auto;
    transform: none;
    transform-box: view-box;
    transform-origin 98.8px: 33.8px;
    transform-style: flat;
    transition-behavior: normal;
    transition-delay: 0s;
    transition-duration: 0s;
    transition-property: all;
    transition-timing-function: ease;
    translate: none;
    unicode-bidi: bidi-override;
    user-select: auto;
    vector-effect: none;
    vertical-align: baseline;
    view-timeline-axis: block;
    view-timeline-inset: auto;
    view-timeline-name: none;
    view-transition-class: none;
    view-transition-name: none;
    visibility: visible;
    white-space-collapse: preserve;
    widows: 2;
    width: 197.6px;
    will-change: auto;
    word-break: normal;
    word-spacing: 0px;
    writing-mode: horizontal-tb;
    x: 0px;
    y: 0px;
    z-index: auto;
    zoom: 1;
    -webkit-border-horizontal-spacing: 0px;
    -webkit-border-vertical-spacing: 0px;
    -webkit-box-align: stretch;
    -webkit-box-decoration-break: slice;
    -webkit-box-direction: normal;
    -webkit-box-flex: 0;
    -webkit-box-ordinal-group: 1;
    -webkit-box-orient: horizontal;
    -webkit-box-pack: start;
    -webkit-box-reflect: none;
    -webkit-font-smoothing: subpixel-antialiased;
    -webkit-line-clamp: none;
    -webkit-locale: auto;
    -webkit-mask-box-image-outset: 0;
    -webkit-mask-box-image-repeat: stretch;
    -webkit-mask-box-image-slice 0: fill;
    -webkit-mask-box-image-source: none;
    -webkit-mask-box-image-width: auto;
    -webkit-mask-position-x: 0%;
    -webkit-mask-position-y: 0%;
    -webkit-perspective-origin-x
    -webkit-perspective-origin-y
    -webkit-print-color-adjust: economy;
    -webkit-rtl-ordering: logical;
    -webkit-tap-highlight-color rgba(0, 0, 0,: 0.18);
    -webkit-text-combine: none;
    -webkit-text-decorations-in-effect: none;
    -webkit-text-fill-color rgb(68, 68,: 68);
    -webkit-text-security: none;
    -webkit-text-stroke-color rgb(68, 68,: 68);
    -webkit-text-stroke-width: 0px;
    -webkit-transform-origin-x:;
    -webkit-transform-origin-y:;
    -webkit-transform-origin-z:;
    -webkit-user-drag: auto;
    -webkit-user-modify: read-only;
    -webkit-border-image: none;
    -webkit-ruby-position: before;
    -webkit-text-orientation: vertical-right;
}
0

3Answer

追加で提示された計算済みスタイルのうち
以下が原因ではないかと。
unicode-bidi: bidi-override;

@rye_442 さんが提示されているものに追加して再現および改善の確認ができました

<head>
  <meta charset="UTF-8">
  <style type="text/css">
+   textarea { /* 既存のスタイルシート代わり */
+     unicode-bidi: bidi-override;
+   }
    textarea {
      display: inline-block;
      box-sizing: border-box;
      width: 100%;
      min-height: 20px;
      border: 2px solid rgba(68, 68, 68, .1);
      border-radius: 0;
      padding: .25em .5em;
      resize: none;
      overflow: auto;
      overflow-y: hidden;
+     unicode-bidi: normal; /* この上書き指定を削除すると問題が再発します */
    }
  </style>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script>
    $(document).ready(function() {
      $("textarea").each(function() {
        var elm = $(this);
        const ch = elm.height();
        elm.on("input", function() {
          elm.height(ch);
          const sh = elm.get(0).scrollHeight;
          elm.height(sh);
        });
      });
    });
  </script>
</head>
<body>
  <textarea placeholder="本文を入力..."></textarea>
</body>

上記のような不具合再現を確認できる最小のコードを自分で作って確認すると、自力での解決に繋がると思います。
再現のための最小部分を探すことが問題の切り分け調査になり、「どこをon/offすれば変わるか」が原因そのものにあたります。

今回は「計算済みスタイルを追加すれば再現する」と分かった時点で、実は調査はほぼ終わっているようなものです。

  1. 再現用の最小コードに計算済みスタイルを追加して、不具合再現を確認
  2. 計算済みスタイルの上半分を消して、再現するか確認
    計算済みスタイルの下半分を消して、再現するか確認
  3. 再現しなかった方は完全に削除して、2.を繰り返す

これだけで、unicode-bidiがなにかは分からなくても特定が出来ました。

1Like

Comments

  1. @shulmj_

    Questioner

    こちらで解決できました!
    ありがとうございます。

Google Chrome 124.0.6367.92(Official Build) (64 ビット)で、以下のhtmlで試したところ、特に問題は発生しませんでした。
Chromeのバージョンを変えても発生しますか?

<head>
  <style type="text/css">
    textarea {
      display: inline-block;
      box-sizing: border-box;
      width: 100%;
      min-height: 20px;
      border: 2px solid rgba(68, 68, 68, .1);
      border-radius: 0;
      padding: .25em .5em;
      resize: none;
      overflow: auto;
      overflow-y: hidden;
    }
  </style>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script>
    $(document).ready(function() {
      $("textarea").each(function() {
        var elm = $(this);
        const ch = elm.height();
        elm.on("input", function() {
          elm.height(ch);
          const sh = elm.get(0).scrollHeight;
          elm.height(sh);
        });
      });
    });
  </script>
</head>
<body>
  <textarea placeholder="本文を入力..."></textarea>
</body>
0Like

Comments

  1. 124.0.6367.92(Official Build) (64 ビット)
    私も↑のバージョンで試してみましたが、問題の動作は発生しませんでした。

    Google Chrome 126.0.6439.0(Official Build)dev
    devバージョンってのがちょっと臭いますね・・・
    通常のバージョンではいかがですか?

  2. @shulmj_

    Questioner

    @iiokazuya @rye_442
    ご回答ありがとうございます。
    こちらでDev版ではない124.0.6367.63でテストしましたが、同様の症状を再現しました。

    既存のウェブサイトにChrome拡張機能を利用して、スクリプトやスタイルシートを挿入する形をとっているので、そのページ側の問題かも知れません。
    以下に適用されているルールを掲載し、質問本文にブラウザの開発者向けモードの「計算済みタブ」の内容を追記させて頂きました。

    CSS
    /* ウェブサイト側で挿入されたスタイルシート */
    textarea {
        overflow: auto;
    }
    
    button, input, optgroup, select, textarea {
        font: inherit;
        margin: 0;
        color: inherit;
    }
    
    *, :after, :before {
        -webkit-box-sizing: inherit;
        box-sizing: inherit;
        unicode-bidi: inherit;
    }
    
    /* ユーザー エージェント スタイルシート */
    textarea {
        font-style: ;
        font-variant-ligatures: ;
        font-variant-caps: ;
        font-variant-numeric: ;
        font-variant-east-asian: ;
        font-variant-alternates: ;
        font-variant-position: ;
        font-weight: ;
        font-stretch: ;
        font-size: ;
        font-family: monospace;
        font-optical-sizing: ;
        font-kerning: ;
        font-feature-settings: ;
        font-variation-settings: ;
        text-rendering: auto;
        color: fieldtext;
        letter-spacing: normal;
        word-spacing: normal;
        line-height: normal;
        text-transform: none;
        text-indent: 0px;
        text-shadow: none;
        display: inline-block;
        text-align: start;
        appearance: auto;
        -webkit-rtl-ordering: logical;
        resize: -internal-textarea-auto;
        cursor: text;
        overflow-wrap: break-word;
        background-color: field;
        column-count: initial !important;
        margin: 0em;
        border-width: 1px;
        border-style: solid;
        border-color: light-dark(rgb(118, 118, 118), rgb(133, 133, 133));
        border-image: initial;
        padding: 2px;
        white-space: pre-wrap;
    }
    

こんな感じでどうでしょうか?

<textarea oninput="style.height='auto';style.height=scrollHeight+'px'" style="resize:none;overflow:hidden;width:100%" rows=1></textarea>
0Like

Your answer might help someone💌