티스토리 뷰
나중에 쓸지도 몰라서 해시태그를 추가하는 컴포넌트를 만들었다.
기본적인 기능은 네이버 블로그의 해시태그 기능을 보며 참고했다.
아래는 내가 만든 해시태그 첫 진입시 UI
이걸 클릭하게되면..
이런식으로 focus가 input에 들어가게된다.
enter
나 space
를 누를때마다 태그가 추가되고, 특수문자
나 중복
은 안된다.
위의 이미지처럼 중복태그나, 특수문자 입력 후 태그 추가시(enter, space) 경고창이 뜬다.
아래는 만든 해시태그를 불러올 부모 컴포넌트이다.
// 부모 컴포넌트
<hashtags :placeholder="#해시태그를작성하세요"></hashtags>
바인딩된 placeholder로 기본 설명을 넣을 수 있다. 기본값이 들어가 있으니 굳이 쓰고 싶지 않다면 제외해도 된다.
// 부모 js
import Hashtags from '@/components/Hashtags';
components : {
Hashtags : Hashtags
}
그리고 본 소스. 아래는 구현해놓은 vue code sandbox 를 첨부해놓았다.
<div class="comp_hashtag" @click="setHashtags" ref="group">
<p class="help" v-if="helpVisible">{{ defaultPlaceholder }}</p>
<!-- Hashtags -->
<div class="tags" v-if="!helpVisible">
<input
type="text"
class="fake"
ref="fake"
@keydown.backspace.prevent="deleteTag(focusIndex)"
@keydown.delete.prevent="deleteTag(focusIndex)"
/>
<span
class="tag"
v-for="(row, index) in tags"
:key="index"
:class="{active: row.select}"
@click="selectTag(index)"
>{{ row.value }}</span
>
</div>
<!--// Hashtags -->
<div class="inp" v-show="!helpVisible">
<input
type="text"
ref="input"
v-model.trim="value"
@focus="initSelect"
@keydown.space.prevent="addHashTags"
@keydown.enter.prevent="addHashTags"
@keydown.backspace="initErrorMsg"
@keydown.delete="initErrorMsg"
placeholder="태그입력"
/>
</div>
<transition
enter-active-class="animate__animated animate__fadeInDown animate__faster"
leave-active-class="animate__animated animate__fadeOut"
>
<p class="noti" v-if="this.errorMsg">{{ errorMsg }}</p>
</transition>
</div>
export default {
name: 'Hashtags',
props: ['placeholder'],
data() {
return {
defaultPlaceholder: this.placeholder ? this.placeholder : '#추천태그 #특수문자제외',
errorMsg: null,
focusIndex: null,
helpVisible: true,
tags: [],
value: '',
};
},
methods: {
setVisible() {
return (this.helpVisible = false);
},
async setHashtags() {
if (this.tags.length > 0) {
return;
}
const result = await this.setVisible();
if (!result) this.$refs.input.focus();
},
addTag() {
this.tags.push({value: this.value, select: false});
return true;
},
unselectTag() {
this.tags.forEach(tag => (tag.select = false));
},
selectTag(idx) {
if (this.tags.some(tag => tag.select)) {
this.unselectTag();
}
this.tags[idx].select = !this.tags[idx].select;
if (!this.tags[idx].select) {
this.initSelectIndex();
return;
}
this.$refs.fake.focus();
this.focusIndex = idx;
},
deleteTag(idx) {
if (idx === null) {
return;
}
this.initSelectIndex();
this.tags.splice(idx, 1);
},
initSelect() {
if (!this.tags.some(tag => tag.select)) {
return;
}
this.unselectTag();
this.initSelectIndex();
},
initSelectIndex() {
this.focusIndex = null;
},
initErrorMsg() {
this.errorMsg = null;
},
validate() {
if (this.tags.some(tag => tag.value === this.value)) {
return '중복된 단어를 입력하셨습니다.';
}
const regex = /[~!@#$%^&*()+|<>?:{},.="':;/-]/;
if (regex.test(this.value)) {
return '특수문자는 태그로 등록할 수 없습니다.';
}
return false;
},
async addHashTags(event) {
// CASE 공백
if (event.target.value === '') {
this.initErrorMsg();
event.target.focus();
return;
}
// CASE 유효성(중복,특문)
const resultMsg = await this.validate();
if (resultMsg) {
this.errorMsg = resultMsg;
this.$refs.input.focus();
return;
}
await this.addTag();
this.errorMsg = null;
this.value = null;
this.$refs.input.focus();
},
documentClick(e) {
if (this.$refs.group !== e.target && !this.$refs.group.contains(e.target)) {
this.unselectTag();
this.errorMsg = null;
}
},
},
mounted() {
document.addEventListener('mousedown', this.documentClick);
},
};
구현 :
https://codesandbox.io/s/friendly-morning-6uhud?from-embed=&file=/src/components/Hashtags.vue
해당 컴포넌트는 모바일은 고려가 되어있지 않다.
왜냐하면 추가된 해시태그는 backspace
나 delete
로 지우게 되어있기 때문이다.
추후에 좀더 추가해서 선택에 따라 모바일 적용도 해놔야겠다.
*댓글과 좋아요는 힘이됩니다.
'FRONT > Vue.js' 카테고리의 다른 글
SPA(single page application)의 GA 적용기 (google analytics) (0) | 2021.07.29 |
---|---|
Vue에서 상태관리란? Vuex로 정리하기 (0) | 2021.07.27 |
유효성 검증을 위한 vee-validate 플러그인 (0) | 2021.07.15 |
vue에서 textarea의 줄바꿈 글을 표현할 때 (0) | 2021.07.15 |
vue-awesome-swiper 의 IE issue Syntax error (0) | 2021.07.07 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 웹접근성
- component
- WAI-ARIA
- youtube
- 포세이돈되기까지
- javascript
- js
- curring
- 퍼블리셔
- css3
- 리액트생명주기
- ES5
- vue.js
- 환경설정
- API
- seo
- 수영전후기
- Issue
- 수영하기전후기
- props
- 웹퍼블리싱
- frontend
- Aria
- 웹퍼블리셔
- 자바스크립트
- 퍼블리싱
- jQuery
- vue
- 커리패턴
- jeonst
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함