클래스형은 너무 어렵다.. 그냥 어떻게 돌아가는지만 구조만 알아두자..
자바스크립트 강의
이벤트 핸들러 함수 & “this”
- JavaScript에서 함수를 호출할 때, 함수 내에서 this 키워드가 가리키는 대상은 함수를 호출하는 방법에 따라 달라진다
- addEventListener() 메서드를 사용하여 이벤트 핸들러 함수를 등록한 경우 ⇒이벤트 핸들러 함수 내에서 this는 해당 요소를 가리킨다
const myButton = document.querySelector('#my-button');
myButton.addEventListener('click', function() {
console.log('버튼이 클릭되었습니다.');
console.log(this); // myButton 요소 출력
});
- bind()를 사용하여 함수 내에서 this를 특정 값으로 고정 ⇒bind()는 첫 번째 매개변수로 함수 내에서 this 키워드가 가리킬 대상을 전달받는다
const myButton = document.querySelector('#my-button');
function handleClick() {
console.log('버튼이 클릭되었습니다.');
console.log(this); // myButton 요소 출력
}
myButton.addEventListener('click', handleClick.bind(myButton));
- 화살표 함수를 사용하여 이벤트 핸들러 함수를 등록하는 경우 ⇒this는 해당 함수를 감싸고 있는 스코프의 this를 따른다,this가 항상 이벤트를 발생시킨 DOM 요소를 가리키도록 보장할 수는 없다 event.target속성을 사용하여 이벤트를 발생시킨 DOM 요소에 대한 참조를 얻는 것이 좋다
const myButton = document.querySelector('#my-button');
myButton.addEventListener('click', () => {
console.log('버튼이 클릭되었습니다.');
console.log(this); // 전역 객체(window) 출력
});
드래그앤 드롭
- drag and dropAPI를 사용한다dragstart, drag, dragenter, dragleave, dragover, drop 등의 이벤트를 제공
- 개체를 드래그 가능하게 만들려면 해당 요소에서 ”draggable=true”를 설정해야한다
- 이벤트 핸들러 함수에서 event.preventDefault()를 호출하여 브라우저의 기본 동작을 막아야 한다 ⇒브라우저에서 요소를 드래그하면 기본적으로 페이지가 새로고침되거나 링크가 열리는 등의 동작을 막기 위한 것(기본값은 드롭 작업을 취소한다)
class DOMHelper {
static clearEventListeners(element) {
const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}
static moveElement(elementId, newDestinationSelector) {
const element = document.getElementById(elementId);
const destinationElement = document.querySelector(newDestinationSelector);
destinationElement.append(element);
element.scrollIntoView({ behavior: "smooth" });
}
}
clearEventListeners(element 주어진 요소에서 모든 이벤트 리스너를 제거하는 데 사용)
- cloneNode() 메소드를 사용하여 주어진 요소를 복제 true 매개변수는 하위 요소와 모든 하위 요소의 이벤트 리스너를 포함한 모든 자식 요소를 복제하도록 지시한다
- replaceWith() 메소드를 사용하여 원래 요소를 복제된 요소로 대체하여 반환한다
moveElement(elementId, newDestinationSelector) 주어진 ID를 가진 요소를 newDestinationSelector로 이동시키는 데 사용
- getElementById() 사용하여 주어진 ID를 가진 요소를 가져 온다
- querySelector() 를 사용하여 새로운 목적지 요소를 가져 온다
- append()를 사용하여 이동할 요소를 목적지에 추가
scrollIntoView() 메소드를 사용하여 이동한 요소가 브라우저의 뷰포트에 표시되도록 스크롤하고 { behavior: "smooth" } 매개변수는 부드러운 스크롤 동작을 사용하도록 지시
element.scrollIntoView();
element.scrollIntoView(alignToTop); // Boolean parameter
element.scrollIntoView(scrollIntoViewOptions); // Object parameter
true : element 요소의 상단을 기준으로 스크롤을 이동한다.
false : element 요소의 하단을 기준으로 스크롤을 이동한다.
behavior : 전환 에니메이션 정의 (auto || smooth)
block : 수직 정렬 (start || center || end || nearest)
inline : 수평 정렬 (start || center || end || nearest)
class Component {
constructor(hostElementId, insertBefore = false) {
if (hostElementId) {
this.hostElement = document.getElementById(hostElementId);
} else {
this.hostElement = document.body;
}
this.insertBefore = insertBefore;
}
detach() {
if (this.element) {
this.element.remove();
// this.element.parentElement.removeChild(this.element);
}
}
attach() {
this.hostElement.insertAdjacentElement(
this.insertBefore ? "afterbegin" : "beforeend",
this.element
);
}
}
- ID가 제공되면 document.getElementById를 사용하여 호스트 엘리먼트를 찾고 ID가 제공되지 않으면 document.body 엘리먼트를 호스트 엘리먼트로 사용한다
- insertBefore 매개 변수를 받고, true로 설정할 경우 새로운 엘리먼트를 hostElement의 첫번째 자식 노드 앞에 삽입하고, 그렇지 않으면 마지막 자식 노드 뒤에 삽입한다.
- detach()는 현재 element를 가지고 있을 경우 해당 엘리먼트를 DOM에서 삭제
- insertAdjacentElement(position, element)를 사용하여 beforeend 또는 afterbegin 위치에 삽입할 수 있다. 이것은 호스트 엘리먼트의 새로운 자식 엘리먼트로 추가된다
- 'beforebegin': targetElement자기 앞에.
- 'afterbegin': 의 첫 번째 자식 바로 앞 targetElement.
- 'beforeend': 의 targetElement마지막 자식 바로 뒤에
- 'afterend': 자체 뒤에 targetElement.
class Tooltip extends Component {
constructor(closeNotifierFunction, text, hostElementId) {
super(hostElementId);
this.closeNotifier = closeNotifierFunction;
this.text = text;
this.create();
}
closeTooltip = () => {
this.detach();
this.closeNotifier();
};
create() {
const tooltipElement = document.createElement("div");
tooltipElement.className = "card";
const tooltipTemplate = document.getElementById("tooltip");
const tooltipBody = document.importNode(tooltipTemplate.content, true);
tooltipBody.querySelector("p").textContent = this.text;
tooltipElement.append(tooltipBody);
const hostElPosLeft = this.hostElement.offsetLeft;
const hostElPosTop = this.hostElement.offsetTop;
const hostElHeight = this.hostElement.clientHeight;
const parentElementScrolling = this.hostElement.parentElement.scrollTop;
const x = hostElPosLeft + 20;
const y = hostElPosTop + hostElHeight - parentElementScrolling - 10;
tooltipElement.style.position = "absolute";
tooltipElement.style.left = x + "px"; // 500px
tooltipElement.style.top = y + "px";
tooltipElement.addEventListener("click", this.closeTooltip);
this.element = tooltipElement;
}
}
생성자 함수는 상위 클래스인 컴포넌트 클래스의 생성자 함수를 호출하고, closeNotifier, text, 그리고 create() 함수를 호출한다
- closeTooltip() 툴팁을 닫을 때, detach() 함수를 호출하고, closeNotifier() 함수도 호출
- create() 툴팁을 담을 div 엘리먼트를 만들고, 템플릿 엘리먼트를 가져와서 복사한 후, 텍스트를 삽입,툴팁의 위치를 계산하여 설정 이벤트 리스너를 추가하고, 생성된 툴팁 엘리먼트를 this.element에 할당
class ProjectItem {
hasActiveTooltip = false;
constructor(id, updateProjectListsFunction, type) {
this.id = id;
this.updateProjectListsHandler = updateProjectListsFunction;
this.connectMoreInfoButton();
this.connectSwitchButton(type);
this.connectDrag();
}
showMoreInfoHandler() {
if (this.hasActiveTooltip) {
return;
}
const projectElement = document.getElementById(this.id);
const tooltipText = projectElement.dataset.extraInfo;
const tooltip = new Tooltip(
() => {
this.hasActiveTooltip = false;
},
tooltipText,
this.id
);
tooltip.attach();
this.hasActiveTooltip = true;
}
//어떤요소가 드래그 된건지 연결
connectDrag() {
document.getElementById(this.id).addEventListener("dragstart", (event) => {
event.dataTransfer.setData("text/plain", this.id);
event.dataTransfer.effectAllowed = "move";
});
}
//.....
}
}
DataTransfer.setData() 드래그 조작의 설정 drag data 지정된 데이터 유형. 주어진 유형에 대한 데이터가
존재하지 않으면,types 목록 의 마지막 항목 이 새로운 유형이되도록 드래그 데이터 저장소의 끝에 추가하고
주어진 유형에 대한 데이터가 이미 존재하는 경우 기존 데이터가 동일한 위치에 교체 한다
같은 유형의 데이터를 교체 할 때 types 목록 의 순서는 변경되지 않는다
dataTransfer.effectAllowed ()
- none: 허용되는 효과가 없음을 의미합니다. 드래그 소스는 이동할 수 없다
- copy: 드롭 대상에게 복사, 드래그 소스의 원본은 그대로 유지
- link: 드롭 대상에게 링크, 일반적으로 드롭 대상이 URL이거나 웹 페이지 일 때 사용
- move: 드롭 대상으로 이동, 드래그 소스의 원본은 제거
- copyMove: 드롭 대상에 복사되거나 이동, 드래그 소스의 원본은 그대로 유지될 수도, 제거될 수도 있다
- linkMove: 드롭 대상에게 링크되거나 이동,일반적으로 드롭 대상이 URL이거나 웹 페이지 일 때 사용
class ProjectList {
projects = [];
constructor(type) {
this.type = type;
const prjItems = document.querySelectorAll(`#${type}-projects li`);
for (const prjItem of prjItems) {
this.projects.push(
new ProjectItem(prjItem.id, this.switchProject.bind(this), this.type)
);
}
console.log(this.projects);
this.connectDroppable();
}
//드롭가능한 이벤트 리스닝
connectDroppable() {
//li는 삭제해주고 ul태그에대한 엑세스가 생긴다
const list = document.querySelector(`#${this.type}-projects ul`);
//id 읽는건 불가능,유형만 알수 있다
list.addEventListener("dragenter", (e) => {
if (e.dataTransfer.types[0] === "text/plain") {
//드롭이 가능한지 알수 있는 시각적 요소 추가
list.parentElement.classList.add("droppable");
e.preventDefault();
}
});
//dragover꼭 있어야 하고 preventDefault를 호출해야함
list.addEventListener("dragover", (e) => {
if (e.dataTransfer.types[0] === "text/plain") {
e.preventDefault();
}
});
list.addEventListener("dragleave", (e) => {
if (e.relatedTarget.closest(`#${this.type}-projects ul`) !== list) {
list.parentElement.classList.remove("droppable");
}
});
list.addEventListener("drop", (e) => {
const prjId = e.dataTransfer.getData("text/plain");
if (this.projects.find((p) => p.id === prjId)) {
return;
}
document
.getElementById(prjId)
.querySelector("button:last-of-type")
.click();
list.parentElement.classList.remove("droppable");
// event.preventDefault(); // not required
});
}
dragenter 이벤트는 드래그한 요소가 해당 목록 위에 진입할 때 발생
- dataTransfer.types를 확인하여 드래그한 요소의 타입이 "text/plain" 인 경우에만 드롭 가능한 시각적 요소(.droppable 클래스를 가진 부모 요소)를 추가하고, 이벤트의 기본 동작을 막는다
dragover이벤트는 드래그한 요소가 해당 목록 위에 있을 때 발생
dragleave이벤트는 드래그한 요소가 해당 목록에서 벗어날 때 발생
e.relatedTarget.closest는 e.relatedTarget 요소의 가장 가까운 조상 요소를 찾는 JavaScript 메소드이다
- relatedTarget.closest를 사용하여 드래그한 요소가 해당 목록에 속해 있는지 확인하고, 해당 목록 밖으로 벗어날 경우 시각적 요소를 제거
drop이벤트는 드래그한 요소를 해당 목록에 드롭할 때 발생
- dataTransfer.getData를 사용하여 드래그한 요소의 id 값을 가져와 this.projects 배열에서 해당 id값을 가진 항목이 이미 존재하는지 확인
- 이미 존재하는 경우에는 이벤트의 기본 동작을 막는다
- 존재하지 않을 경우에는 해당요소의 버튼을 클릿하여 해당 요소를 해당목록으로 이동시키고 시각적 요소를 제거
'Javascript' 카테고리의 다른 글
자바스크립트 this 와 call, apply, bind (6) | 2023.04.03 |
---|---|
생성자 함수에 의한 객체 생성 (6) | 2023.03.09 |
간단한 js 무한스크롤과 이벤트위임 (6) | 2023.03.03 |
AJAX (서버와 클라이언트 통신) (7) | 2023.02.17 |
location.href / location.replace() (0) | 2022.11.29 |