프론트엔드💛

[Vue3] 여러 Input 중 특정 Input에 focus 시키기

dalin❤️ 2022. 7. 18. 20:40

상황 🤦‍♀️

페이지에서 특정 Input 에 focus 를 해주고 싶다. (회원가입 폼에서 완료 버튼 클릭했는데, 필수로 입력해야 하는 이름을 입력하지 않아서 이름 인풋에 포커스해주고 싶을 때 등등) 그런데 이 페이지에는 여러 InputGroup이 있고, 그 안에 Input들이 여러 개 있다.

몇번 InputGroup의 몇번 Input인지는 아는데, 어떻게 그 input에 focus를 해주지??

사실 회사에서 비슷한 작업을 하고 있는데 이 부분을 모르겠다... (회사 코드는 훨씬 복잡하고 몇번 InputGroup의 몇번 Input을 포커스해야 하는지 알아내는 것도 오래 걸렸다..! 아래 내용, 코드는 내가 공부용으로 간단하게 만든 것~~ !! )

 

 

설명 ❤️

완성 ~!! 

 

Message 부분에서 몇 번 InputGroup의 몇 번 Input에 포커스 해줘야 하는지 판단한다. (실제 코드에서는 그걸 판단하는 로직도 복잡했는데, 여기서는 간단히 임의의 수로 정했다! 

그리고 Message에서 provide로 두 숫자를 넘겨주고, Input에서 inject로 받는다.

InputGroup은 자신이 몇번째 InputGroup인지 알 수 있게 props를 받는다. Input에서는 자신이 몇번째 InputGroup의 몇번째 Input인지 알 수 있게 props를 받는다.

Input은 자신의 위치와 타겟의 위치를 비교해서, 자신이라면 focus를 한다. 일단 이 코드에서는 onMounted에만 한번 포커스하게 했다. 근데 실제에서는 필요한 상황에 포커스를 시켜주면 될 것 같다 ~ 

코드 👩‍💻

Message.vue

<template>
  <div>
    <h1 class="text-center">메시지</h1>
    <section class="text-center">
      <InputGroup :index="0" :attrs="inputAttrs" ref="inputsRef1" />
      <hr />
      <InputGroup :index="1" :attrs="inputAttrs2" ref="inputsRef2" />
      <li>
        <input type="text" ref="input" />
      </li>
    </section>
  </div>
</template>

<script lang="ts" setup>
import { ref, provide } from "vue";

import InputGroup from "@/components/InputGroup.vue";
const inputAttrs = [
  {
    label: "nickname",
    type: "text",
    placeholder: "nickname을 입력하세요",
    value: "",
    index: 0,
  },
  {
    label: "message",
    type: "text",
    placeholder: "message을 입력하세요",
    value: "",
    index: 1,
  },
];

const inputAttrs2 = [
  {
    label: "email",
    type: "text",
    placeholder: "email을 입력하세요",
    value: "",
    index: 0,
  },
  {
    label: "mbti",
    type: "text",
    placeholder: "mbti를 입력하세요",
    value: "",
    index: 1,
  },
];

const targetGroupIndex = ref<number>(0);
const targetItemIndex = ref<number>(1);

provide("targetGroupIndex", targetGroupIndex.value);
provide("targetItemIndex", targetItemIndex.value);
</script>

<style></style>

 

 

InputGroup.vue

<template>
  <div>
    <ul>
      <li v-for="(item, idx) in props.attrs" :key="idx" ref="inputsRef">
        <Input
          :groupIndex="props.index"
          :itemIndex="idx"
          :attr="item"
          @setValue="setValue"
        />
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, computed, watchEffect } from "vue";
import Input from "./Input.vue";

const props = defineProps<{
  index?: number;
  attrs: Array<{
    label?: string;
    type: string;
    placeholder?: string;
    value?: string;
  }>;
}>();

const setValue = (content: string) => {
  console.log(content);
};
</script>

 

Input.vue

<template>
  <input
    :name="props.attr.label"
    :type="props.attr.type"
    :placeholder="props.attr.placeholder"
    :value="props.attr.value"
    @input="(event) => setValue(event)"
    ref="inputEl"
  />
</template>

<script setup lang="ts">
import { ref, inject, onMounted, watch } from "vue";

const emit = defineEmits(["setValue"]);

const props = defineProps<{
  groupIndex?: number;
  itemIndex?: number;
  attr: {
    label?: string;
    type: string;
    placeholder?: string;
    value?: string;
    index?: number;
  };
}>();

const inputValue = ref<string>("");
const inputEl = ref<HTMLInputElement | null>(null);

const setValue = (event: InputEvent | Event): void => {
  const target: HTMLInputElement = event.target as HTMLInputElement;
  inputValue.value = target.value;
  emit("setValue", inputValue.value);
};

const targetGroupIndex = inject("targetGroupIndex");
const targetItemIndex = inject("targetItemIndex");

const focusOnTargetInputEl = () => {
  if (
    targetGroupIndex === props.groupIndex &&
    targetItemIndex === props.itemIndex
  ) {
    inputEl.value?.focus();
  }
};

onMounted(() => {
  focusOnTargetInputEl();
});
</script>

<style lang="scss" scoped>
input {
  border: 1px solid black;
  border-radius: 4px;
  padding: 4px;
  margin: 4px;
}
</style>

 

이 코드가 포함된 내 레포 ~!
https://github.com/tjdls111/birthday_vue/commit/d2e917c15201090545f4316a7edb429371fd376f

 

feat:인풋에 포커스 맞추기 · tjdls111/birthday_vue@d2e917c

Show file tree Hide file tree Showing 3 changed files with 36 additions and 18 deletions.

github.com

 

 

참고 자료 😍

https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management

 

Focus management with Vue refs - Learn web development | MDN

So that's it for focus management, and for our app! Congratulations for working your way through all our Vue tutorials. In the next article we'll round things off with some further resources to take your Vue learning further.

developer.mozilla.org

=> 이걸 보면 Vue에서 어떻게 focus 를 하면 될지에 도움이 된다. focus를 해줄 때 이해해야 하는 가상 돔, ref 등을 알려준다.

- vue에서 focus를 하는 등 돔 요소를 조작할 때 refs를 사용한다.(바닐라 JS에서 getElementById 하듯이)

- 컴포넌트에서 refs를 사용하고 싶으면, 속성에 refs를 추가한다. 구별할 수 있는 값을 줘야 한다. (ref="구별할 수 있는 이름")

 

아래 자료는 ref 를 이해하는 데 도움이 된다.

https://vuejs.org/guide/essentials/template-refs.html

 

Template Refs | Vue.js

 

vuejs.org

 

 

타입 스크립트 + composition api 는 이거 보기 ! !

https://vuejs.org/guide/typescript/composition-api.html#typing-template-refs

 

TypeScript with Composition API | Vue.js

 

vuejs.org

 

후..ㅎㅎ 이렇게 긴 블로그 글은 처음 써보는 것 같네요! 읽어주셔서 감사합니다~ 혹시 궁금한 부분이나 오류가 있으면 알려주세요 :)

728x90