[Vue.js] textarea 자동 높이 조절 하는법(autosize)

하수도키

·

2020. 11. 2. 19:50

728x90
반응형
SMALL
  • textarea를 사용할때 내용 길이에 따라 자동으로 높이가 조절 되는 방법을 살펴보자.

개요

  • Vue를 기반으로 설명한다.
  • 총 3개의 방법을 알려준다.
  • 이 포스팅은 번역/의역/오역 다수 같으므로 이해가 되지 않거나 자세히 알고 싶으면 아래 링크를 통해 살펴보자.
    Vuejs-auto-size

1번째 방법 : window.addEventlistener 사용

// 자동 높이 조절을 하고 싶은 textarea가 있는 컴포넌트
// 여기서는 ResizeByClass.vue 이다.
<template>
  <textarea class="textarea js-autoresize"></textarea>
</template>
<script>
import { setResizeListeners } from "../helpers/auto-resize.js";
export default {
  name: "ResizeByClass",
  mounted() {
    setResizeListeners(this.$el, ".js-autoresize");
  }
};
</script>
//  auto-resize.js
function resize() {
  this.style.height = "auto";
  this.style.height = `${this.scrollHeight}px`;
}
export const setResizeListeners = ($el, query) => {
  const targets = $el.querySelectorAll(query);
  targets.forEach(target => {
    target.style.height = `${target.scrollHeight}px`;
    target.addEventListener("input", resize);
  });
};

단점

  • 동적으로 추가된 textarea 에는 적용이 되지 않는다. 동적으로 추가된 textarea 에 적용하려면 다시 이벤트 리스너를 추가해줘야 된다. 이때, 기본에 있던 textarea 에 이벤트리스너가 중복되지 않게 주의해야 한다.
  • 이벤트 리스너가 절대 제거 되지 않는다. 따라서 이벤트 리스너를 제거하는 로직이 필요하다.
  • 컴포넌트가 마운트되고 textarea 가 그려질때까지 기다려야되므로 느리다. 뷰가 이걸 피하는 도구를 준다는데 뭔지는 모르겠다.
  • 클래스명이 필요한데 스타일과 관련이 없고 자바스크립트 셀렉터때문에 추가해야되므로, 적절하지 않다. 클래스명 충돌이 발생할 수도 있다.
  • 위 단점들때문에 최적의 방법이 아니다.

2번째 방법 : mixin 사용

  • 아래처럼 textarea 요소 이벤트에 적용해보자.
<textarea
  class="textarea"
  @input="autoResize"
></textarea>
  • textarea 직접 이벤트 리스너를 연결한다.
  • 이벤트 리스너를 따로 제거할 필요가 없다. Vue 이벤트를 사용하므로 알아서 해준다.
  • 클래스명을 기억할 필요가 없고 메서드, 이벤트명만 기억하면 된다.
// ResizeByMixin.vue
<template>
  <div class="wrapper">
    <textarea
      class="textarea"
      @input="mixin_autoResize_resize"
    ></textarea>
  </div>
</template>
<script>
import mixinAutoResize from "../mixins/autoResize.js";
export default {
  name: "ResizeByMixin",
  mixins: [mixinAutoResize]
};
</script>
// autoResize.js
export default {
  methods: {
    mixin_autoResize_resize(event) {
      event.target.style.height = "auto";
      event.target.style.height = `${event.target.scrollHeight}px`;
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.$el.setAttribute("style", "height",
      `${this.$el.scrollHeight}px`);
    });
  }
};

단점

  • 믹스인을 사용하므로 믹스인 메서드명이 길어진다.(기존 컴포넌트 메서드와 충돌을 방지하기 위해)
  • textarea autosize가 필요한 모든 컴포넌트에 mixin을 추가해야 한다. 해결책은 전역으로 메서드 추가, provide, inject 하는 방법이 있다.

장점

  • 동적으로 추가된 컴포넌트에서 잘 작동한다.
  • 컴포넌트가 마운트 될때까지 기다리지 않고 컴포넌트가 만들어질때 같이 실행된다. 위에보다 빠르다는 말이다.

3번째 방법 : wrapper component 사용

  • mixin과 비슷하지만 template 안에서 사용할수있다.
  • mixin을 등록하는 대신 ResizeAuto 컴포넌트를 등록한다.
  • renderless component이므로 아무것도 render하지 않지만 render 메소드를 사용하여 안에 있는 모든것들을 반환한다.(slot)
  • scoped slot 을 사용하여 resize 메소드에 접근한다. mixin과 같다.
// Wrapper component
<template>
  <div class="wrapper">
    <ResizeAuto>
      <template v-slot:default="{resize}">
        <textarea
          class="textarea"
          @input="resize"
        ></textarea>
      </template>
    </ResizeAuto>
  </div>
</template>
<script>
import ResizeAuto from "./components/ResizeAuto";
export default {
  name: "App",
  components: { ResizeAuto },
};
</script>

// ResizeAuto.vue

export default {
  name: "ResizeAuto",
  methods: {
    resize(event) {
      event.target.style.height = "auto";
      event.target.style.height = `${event.target.scrollHeight}px`;
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.$el.setAttribute(
        "style",
        "height",
        `${this.$el.scrollHeight}px`
      );
    });
  },
  render() {
    return this.$scopedSlots.default({
      resize: this.resize
    });
  }
};

장점

  • mixins과 같다
  • 이벤트 발생을 원하는 모든 textarea 에 적용했다.
  • 뷰 이벤트 리스너를 사용해 별도로 이벤트를 제거할 필요가 없다.
  • 동적으로 추가된 컴포넌트에서도 잘 작동한다.
  • 컴포넌트가 마운트 될때까지 기다리지 않아도 된다.
  • 믹스인과 다르게 네이밍으로 충돌되지 않는다. 템플릿에서 scopedSlot의 이름을 항상 다르게 지정할 수 있기 때문이다. 코드를 리팩토링 할 필요가 없다.???????(Unlike in mixins, using this solution you won’t meet many name conflicts. This is because you can always name upcoming scopedSlots differently in the template. There is no need to refactor your code.)
  • 템플릿 위에서부터 아래로 읽어가면 쉽게 알수 있다. 가독성이 좋다는듯

단점

  • 필요한 모든곳에 컴포넌트를 import 해야된다.(글로벌로 사용하면 해결)
  • 스크립트 resize 메소드를 사용하기가 어렵다.?? (Harder to use method resize in script part. It can be done but it is harder.)

결론

  • Vuejs 기반이지만 1번째 방법에서 함수 부분을 활용하면 일반 자바스크립트에서도 사용이 가능하다.
  • 다양한 관점에서 해결하는 방법을 살펴 봤고, 어떤게 좋다 나쁘다 할 수는 없다. 각각 개인의 생각에 따라 선택하면 된다.
  • 필자는 마지막 3번째를 선택했다고 합니다.
  • 소스 코드는 여기를 참고하자.
    Source Code - codesandbox
728x90
반응형
LIST