VanillaJS를 이용하여 Tooltip Input Component 만들기

/**
 * (required) type : number | text
 * (required) id : text
 * (optional) blank : boolean
 * (optional) blankMessage : text
 * (optional) disabled: boolean
 * (optional) regExp : tooltip message view condition : regExp ( 양끝 / 제거한 값을 넣어줘야함 )
 * (optional) regExpMessage : tooltip message
 * (optional) frontLabel : text
 * (optional) backLabel : text
 * * */
class TooltipInput extends HTMLElement {
  /**
     * constructor : 최초 실행 생성 Method
     * */
  constructor() {
    super();
    this.render();
  }

  /**
     * render() : 해당 tag에 넣을 html tag를 rerendering한다.
     * */
  render() {
    this.innerHTML = this.getTemplate();
  }

  /**
     * 해당 컴포넌트가 mount될 때 불러진다.
     * * */
  connectedCallback() {
    const attrList = this.attributes;
    const input = this.getElementsByClassName('tooltip-input');

    let front = attrList.frontLabel;
    let back = attrList.backLabel;

    if (front === undefined) {
      front = this.getElementsByClassName('front');
      front[0].className = 'display-none';
    }
    if (back === undefined) {
      back = this.getElementsByClassName('back');
      back[0].className = 'display-none';
    }

    /**
         * input eventListener
         * * */
    input[0].addEventListener('input', (e) => {
      const { blank } = e.path[3].attributes;
      const { blankMessage } = e.path[3].attributes;
      const rex = e.path[3].attributes.regExp;
      const rexMessage = e.path[3].attributes.regExpMessage;

      /* input.value 빈칸인 경우 */
      if (e.currentTarget.value.length === 0) {
        if (blank !== null && blank !== undefined && (blank.value === 'true' || blank.value === '')) {
          const tooltipMessage = this.getElementsByClassName('tooltip-message');
          tooltipMessage[0].className = 'tooltip-message';
          if (blankMessage === undefined) {
            tooltipMessage[0].innerHTML = '값을 입력해주세요.';
          } else {
            tooltipMessage[0].innerHTML = blankMessage.value;
          }

          console.log(tooltipMessage[0].classList);
        }
      } else {
        /* input.value 빈칸이 아닌 경우 */
        /* attr 에 정규식이 있는 경우 */
        if (rex !== null && rex !== undefined) {
          const regExp = new RegExp(rex.value);
          console.log(!regExp.test(e.currentTarget.value));
          if (!regExp.test(e.currentTarget.value)) {
            // 정규식 만족 X
            const tooltipMessage = this.getElementsByClassName('tooltip-message');
            tooltipMessage[0].className = 'tooltip-message';
            if (rexMessage !== undefined && rexMessage !== null) {
              tooltipMessage[0].innerHTML = rexMessage.value;
            } else {
              tooltipMessage[0].innerHTML = '조건을 만족하지 않습니다.';
            }
          } else if (blank !== null && blank !== undefined && (blank.value === 'true' || blank.value === '')) {
            const tooltipMessage = this.getElementsByClassName('tooltip-message');
            tooltipMessage[0].className = 'tooltip-message display-none';
          }
        }
      }
    });

    /**
         * disabled 값이 존재하는지 체크
         * disabled 값이 존재 == true 인경우 input의 disabled attr 활성화
         * disabled 값이 존재하지 않거나 ( null ) false인 경우 input의 disabled attr 비활성화(삭제)
         * * */

    if (attrList.getNamedItem('disabled') !== null && (attrList.getNamedItem('disabled').value === 'true' || attrList.getNamedItem('disabled').value === '')) {
      /**
             * disabled 값이 true인 경우 input의 disabled attr 활성화
             * */
      input[0].setAttribute('disabled', 'disabled');
    }

    /**
         * input값이 빈칸인 경우 확인해야하는지 체크
         * blank === true && blankMessage === undefined => "입력값이 필요합니다."
         * blank === true && blankMessage !== undefined => "blankMessage"
         * * */

    if (attrList.getNamedItem('blank') !== null && (attrList.getNamedItem('blank').value === 'true' || attrList.getNamedItem('blank').value === 'blank')) {
      /**
             * blank === true
             * * */
      if (input[0].value === '') {
        const tooltipMessage = this.getElementsByClassName('tooltip-message');
        if (attrList.getNamedItem('blankMessage') !== null) {
          // blank === true && blankMessage === undefined => "입력값이 필요합니다."
          tooltipMessage[0].innerHTML = attrList.getNamedItem('blankMessage').value;
        } else {
          // blank === true && blankMessage !== undefined => "blankMessage"
          tooltipMessage[0].innerHTML = '입력값이 필요합니다.';
        }
      }
    }
  }

  /**
     * 해당 tag에 attributes를 observing한다.
     * * */
  static get observedAttributes() {
    return ['type', 'id', 'blank'];
  }

  /**
     * getTemplate() : rendering될 html tag에 대한 template이다.
     * * */
  getTemplate() {
    let frontLabel;
    let backLabel;

    const type = this.attributes.type.value || '';
    const id = this.attributes.id.value || '';

    frontLabel = this.attributes.frontLabel !== undefined ? this.attributes.frontLabel.value : '';
    backLabel = this.attributes.backLabel !== undefined ? this.attributes.backLabel.value : '';

    return `
            <style>
                input[type="number"]::-webkit-outer-spin-button,
                input[type="number"]::-webkit-inner-spin-button {
                    -webkit-appearance: none;
                    margin: 0;
                }
                
                div{
                    width : 100%;
                }
                
                .tooltip-input{
                    width : 100%;
                    height : 26px;
                    border-radius: 4px;
                    box-shadow: inset 0 3px 2px 0 rgba(49,49,49,0.13);
                    border : solid 1px #b4b4b4;
                    background: #fff;
                    padding-left : 12px;
                    
                    /* font */
                    font-style : normal;
                    font-size : 12px;
                    font-family: 'Roboto';
                    color : #6e6e6e;
                }
                
                .label-text{
                    font-style : normal;
                    font-size : 12px;
                    font-weight : 600;
                    font-family: 'Roboto';
                    color : #6e6e6e;
                }
                
                .disabled{
                    background: #e6e6e6;
                }
                
                .display-none{
                    display : none;
                }
                
                .display-block{
                    display: block;
                }
                
                .tooltip-message{
                    width : auto;
                    height : 16px;
                    border : 1px solid #ede381;
                    background: #f7f2c0;
                    color : #9d870e;
                    
                    white-space: nowrap;
                    
                    position : absolute;
                    right : -15px;
                    top : -32px;
                    padding : 7px 13px 7px 18px;
                    z-index : 30; 
                }
                
                .tooltip-box{
                    min-width : 50px;
                    display: block;
                    position : relative;
                }
                
                .tooltip-box-container{
                    width : 100%;
                    display: flex;
                    flex-direction: row;
                    align-items: center;
                }
                
                .front{
                    width : auto;
                    white-space: nowrap;
                    margin-right : 10px;
                }
                
                .back{
                    width : auto;
                    white-space: nowrap;
                    margin-left : 25px;
                }
                
            </style>
            
            <div class="tooltip-box-container">
                <label for="${id}" class="label-text front">${frontLabel}</label>
                <div class="tooltip-box">
                    <input id="${id}" type="${type}" class="tooltip-input"/>
                    <span class="tooltip-message display-none"></span>
                </div>
                <label for="${id}" class="label-text back">${backLabel}</label>
            </div>
        `;
  }

  /**
     * 해당 컴포넌트가 unmount될 때 불리어 진다.
     * * */
  disconnectedCallback() {

  }

  /**
     * attributes가 수정된다면 수정된 갯수만큼 불리어 진다.
     * * */
  attributeChangedCallback(name, oldValue, newValue) {

  }
}

/**
 * tool-tip 이라는 tag가 customElements에 있는가? 없다면 새롭게 define하라는 코드로,
 * 동일 tag가 2번 define하면 오류가 날 수 있으므로, component가 여러차례 사용되면서 define되는 것을 막기 위한 용도이다.
 * * */

// Define the new web component
if ('customElements' in window) {
  customElements.define('tooltip-input', TooltipInput);
}

과정과 더 자세한 설명은 다음에 쓸 예정

 

참고 블로그 링크 

 

728x90

'Language > Javascript' 카테고리의 다른 글

[ Javascript ] Set Object  (0) 2022.11.02
[ Javascript ] setTimeout, setInterval  (0) 2022.07.27
[ Javascript ] Array.prototype Methods  (0) 2022.03.08
[ Javascript ] async/await  (0) 2022.03.07
[ Javascript ] Promise  (0) 2022.03.06

setTimeout()

setTimeout() 메서드는 만료된 후 함수나 지정한 코드 조각을 실행하는 타이머를 설정한다.

어떤 코드를 바로 실행하지 않고 일정 시간 기다린 후 실행해야하는 경우가 존재한다. 이러한 경우 setTimeout 함수를 사용하여 첫 번째 인자로 받은 실행할 코드를 두번째 인자로 받은 delay time 후에 실행한다. 

delay time은 밀리초(ms) 단위이다. 

var timeoutID = setTimeout( function [, delaty, arg1, arg2, ... ] );
var timeoutID = setTimeout( function [, delay]);
var timeoutID = setTimeout( code [, delay ]);

 

매개변수

function

지정된 delay 시간이 지난 후 실행할 function을 의미한다.

 

code 

함수 대신 문자열을 지정하는 대체 구문으로, 타이머가 만료될 때 코드로 컴파일 후 실행한다. ➡️ 사용을 별로 권장하지 않음.

 

delay  ( optional )

주어진 함수 또는 코드를 실행하기 전에 기다릴 시간이다. 생략하거나 0으로 지정할 경우 "즉시" 실행한다는 뜻이다.

 

arg1, ... , argN

function에 전달할 추가 매개변수이다.

 

반환값

timeoutID는 양의 정수로 setTimeout()이 생성한 타이머를 식별할 때 사용한다. 해당 값을 clearTimeout()에 전달하면 타이머를 취소할 수 있다.

 


setInterval()

setInterval() 메서드는 주기적으로 발생해야 하는 부분이 있을 때 사용한다. 예를 들어 어떤 API로부터 변경된 데이터를 주기적으로 받아온다던가, 특정 부분을 주기적으로 업데이트를 해줘야 하다던가 할때 사용할 수 있다.

setInterval() 메서드는 해당 객체 ID를 반환하므로 setTimeout() 메서드의 clearTimeout()과 같이 clearInterval() 함수를 호출하여 제거할 수 있다. 

 

setInterval() 함수는 어떤 코드를 일정한 시간 간격을 두고 반복해서 실행하고 싶을 때 사용한다. 첫 번째 인자로 실행할 코드를 담고, 두 번째 인자로 반복 주기 시간을 밀리초(ms)단위로 받는다.

 

매개변수, 반환값은 setTimeout과 거의 동일하다.

728x90

'Language > Javascript' 카테고리의 다른 글

[ Javascript ] Set Object  (0) 2022.11.02
[ VanillaJS ] Create Tooltip Input Component using VanillaJS  (0) 2022.10.12
[ Javascript ] Array.prototype Methods  (0) 2022.03.08
[ Javascript ] async/await  (0) 2022.03.07
[ Javascript ] Promise  (0) 2022.03.06

Array.prototype.at(index);

Array.prototype.at(index); method는 parameter로 반환받고 싶은 element의 index값을 넘겨주며, parameter로 음수와 정수 모두 사용가능하며 음수인 경우 배열의 뒤에서부터 indexing한다.

const array = ["apple", "banana", "lemon"];

console.log(array.at(0)); // "apple"
console.log(array[0]); // "apple"

console.log(array.at(-1)); // "lemon"
console.log(array[array.length -1]); // "lemon"

 

Array.prototype.concat(elements);

Array.prototype.concat(elements); method는 기존 배열에 인자로 들어온 요소나 배열을 합하여 새로운 배열을 반환하는 메서드이다.

- 기존 배열을 변경하지 않는다.

- 인자가 추가된 새로운 배열을 반환한다.

const array = ["apple", "samsung"];
const array2 = ["LG"];

console.log(array.concat(array2)); // [ "apple", "samsung", "LG" ]
console.log(array); // ["apple", "samsung"]

const array3 = array.concat(array2);
console.log(array3); // ["apple", "samsung", "LG"]

 

Array.prototype.copyWithin(target, start [,end]);

Array.prototype.copyWithin(target, start [,end]); method는 배열의 일부를 얕게 복사한 뒤, 동일한 배열의 다른 위치에 덮은 후 해당 배열을 반환한다. 이때 배열의 길이는 변경되지 않는다.

const array = ['a','b','c','d','e','f'];
const array2 = ['a','b','c','d','e','f','g'];
array.copyWithin(2,5); // ['a','b','f','d','e','f']
array2.copyWithin(1,2,5); // ['a','c','d','e','e','f','g']

console.log(array); // ['a','b','f','d','e','f']
console.log(array2); // ['a','c','d','e','e','f','g']

 

Array.prototype.entries()

Array.prototype.entries(); method는 배열의 각 인덱스에 대한 키/값 쌍을 가지는 새로운 Array Iterator 객체를 반환한다.

const array = ["apple", "lemon", "orange", "strawberry"];
const iterator = array.entries();

console.log(iterator.next().value)); // [0, "apple"] => Array
console.log(iterator.next().value)); // [1, "lemon"] => Array
console.log(iterator.next().value)); // [2, "orange"] => Array
console.log(iterator.next().value)); // [3, "strawberry"] => Array
console.log(iterator.next().value)); // undefined

 

Array.prototype.every(function)

Array.prototype.every(function) method는 배열의 모든 요소가 주어진 판별함수를 통과하는지 테스트하는 메서드로, 반환값은 boolean이다.

const isTenOver = (element) => element > 10;
const array = [11,12,14,20];

array.every(isTenOver); // true

 

Array.prototype.fill(value [, start [ , end] ] );

Array.prototype.fill() method는 배열의 시작부터 끝 이전까지 정적인 값 하나로 모두 채우는 메서드이다.

* parameter

- value : 배열을 채울 값

- start : 시작 인덱스 기본값 0

- end : 끝 인덱스 기본값 array.length

const array = [1,2,3,4,5];

array.fill(5);
console.log(array); // [5,5,5,5,5]

array.fill('a',2,4); // [5,5,'a','a',5]

 

Array.prototype.filter( callback(element [ , index [ , array ] ] ) [ , thisArg ]);

Array.prototype.filter() 메서드는 매개변수로 주어진 callback 함수의 테스트를 통과한 요소들만 모아 새로운 배열을 반환한다. 

- callback : true 를 반환하면 요소를 유지 false를 반환하면 요소를 버린다. 

- element : 현재 처리할 요소

- index : 현재 처리할 요소의 인덱스

- array : filter를 호출한 배열

- thisArg : callback을 실행할 때 this로 사용하는 값

const number = [1,2,3,"4",5,6];
const newNumber = number.filter( (element) => typeof element === "number" );

console.log(newNumber); // [1,2,3,5,6]

 

Array.prototype.find( callback [ , thisArg ] )

Array.prototype.find() 메서드는 주어진 판별함수를 만족하는 첫 번째 요소의 값을 반환한다. 해당하는 요소가 없다면 undefined를 반환한다.

const array = ["a",3,4,"b"];
const found = array.find((element) => typeof element === "number");

console.log(found); // 3

const fruits = [
	{ name : "banana", quantity : 3 },
    { name : "apple", quantity : 5 },
    { name : "lemon", quantity : 2 }
];

function findApple(fruit){
	return fruit.name === "apple";
}

const Apple = fruits.find(findApple);
console.log(Apple); // {name: 'apple', quantity: 5}

 

Array.prototype.findIndex( callback(element [ , index [ , array ] ]) [ , thisArg ] );

Array.prototype.findIndex() 메서드는 주어진 판별함수를 만족하는 첫 번째 요소의 인덱스를 반환한다. 배열의 첫 번째 요소가 존재하지 않는다면 -1을 반환한다.

const fruits = [
	{ name : "banana", quantity : 3 },
    { name : "apple", quantity : 5 },
    { name : "lemon", quantity : 2 }
];

function findApple(fruit){
	return fruit.name === "apple";
}

function findCherry(fruit){
	return fruit.name === "cherry";
}

const Apple = fruits.findIndex(findApple);
console.log(Apple); // 1

const Cherry = fruits.findIndex(findCherry);
console.log(Cherry); // -1

 

Array.prototype.forEach(callback(currentValue [ , index [ , array ]] ) [ , thisArg ] );

Array.prototype.forEach(); method는 주어진 함수를 배열 요소 각각에 대해 실행한다.

const array = ["apple", "banana", "lemon", "cherry"];
array.forEach( fruit => console.log(fruit) );
// "apple"
// "banana"
// "lemon"
// "cherry"

 

Array.prototype.includes(valueToFind [, fromIndex ]);

Array.prototype.includes(); method는 배열이 특정 요소를 포함하고 있는지를 판별한다. 반환값은 boolean이다.

- valueToFind : 찾고자하는 요소

- fromIndex : 검색을 시작할 위치 

const array = ["apple", "banana", "lemon"];
array.includes("apple"); // true
array.includes("cherry"); // false
array.includes("apple",0); // true
array.includes("apple",2); // false
array.includes("lemon",1); // true

 

Array.prototype.indexOf(searchElement [, fromIndex])

Array.prototype.indexOf(element, [fromIndex]); 메서드는 배열에서 지정된 요소를 찾을 수 있는 첫 번재 인덱스를 반환하고 존재하지 않으면 -1을 반환한다.

const array = [ "a", "b", "c", "d", "e" ];
console.log( array.indexOf("e") ); // 4
console.log( array.indexOf("f") ); // -1
console.log( array.indexOf("a",2) ); // -1

 

Array.isArray(obj);

Array.isArray() 메서드는 메서드의 인자가 Array인지 아닌지를 판별한다. 반환값은 Boolean이다.

const array = [1,2,3];

Array.isArray(array); // true
Array.isArray(3); // false

[ + ] 계속 추가할 예정

Array.prototype.join()

Array.prototype.map()

...

push()

pop()

reduce()

reverse()

shift()

slice()

some()

sort()

unshift()

toString()

values()

 

 

728x90

async/await ?

async/await 은 자바스크립트에서 Promise 객체를 통해 처리하던 비동기 처리를 더 쉽고, 편하게 처리/사용할 수 있도록 나온 ECMAScript 2017 문법이다.

 

async/await 기본문법

async function 함수명() {
	await 비동기처리메서드명();
}

비동기 처리메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 작동한다.

promise 앞에 await keyword를 붙이면, javascript는 promise가 처리될 때 까지 대기하고, 처리가 완료되면 조건에 따라 동작한다.

1. 에러발생 => 예외 생성

2. 에러 미발생 => 프로미스 객체의 result 반환

 

async/await 예외처리

async function 함수명(){
	try{
    	...
    }
    catch(err){
    	console.log(err);
    }
}

try/catch 로 예외처리

 


[ + ] 나중에 내용 더 추가할 예정

728x90

'Language > Javascript' 카테고리의 다른 글

[ Javascript ] setTimeout, setInterval  (0) 2022.07.27
[ Javascript ] Array.prototype Methods  (0) 2022.03.08
[ Javascript ] Promise  (0) 2022.03.06
[ Javascript ] Class { ES6 (ES2015) }  (0) 2022.02.20
[ Javascript ] Set Object  (0) 2022.01.11

+ Recent posts