Skip to content

Commit

Permalink
feat: chapter 1-2 test 통과
Browse files Browse the repository at this point in the history
  • Loading branch information
suzzyso committed Dec 26, 2024
1 parent da7b1c9 commit c816674
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 30 deletions.
70 changes: 67 additions & 3 deletions src/lib/eventManager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,69 @@
export function setupEventListeners(root) {}
const eventListeners = new Map();
let $root = null;

export function addEvent(element, eventType, handler) {}
function handleEvent(e) {
let target = e.target;

export function removeEvent(element, eventType, handler) {}
while (target && target !== $root) {
const handlers = eventListeners.get(e.type)?.get(target);
if (handlers) {
handlers.forEach((handler) => handler(e));
}
target = target.parentElement;
}
}

export function setupEventListeners(root) {
if ($root) return;
$root = root;

eventListeners.forEach((handlers, eventType) => {
$root.removeEventListener(eventType, handleEvent);
$root.addEventListener(eventType, handleEvent);
});

// const supportedEvents = ["click", "input", "change", "submit"];

// supportedEvents.forEach((eventType) => {
// if (!eventListeners.has(eventType)) {
// eventListeners.set(eventType, new Map());
// }

// root.addEventListener(eventType, handleEvent, true);
// });
}

export function addEvent(element, eventType, handler) {
if (!eventListeners.has(eventType)) {
eventListeners.set(eventType, new WeakMap());
}

const handlers = eventListeners.get(eventType);
if (!handlers.has(element)) {
handlers.set(element, new Set());
}

handlers.get(element).add(handler);
}

export function removeEvent(element, eventType, handler) {
const handlers = eventListeners.get(eventType);

if (!handlers) return;

const handlerList = handlers.get(element);
if (handlerList) {
handlerList.delete(handler);
if (handlerList.size === 0) {
handlers.delete(element);
}
}

if (eventListeners.size === 0) {
eventListeners.delete(eventType);
if ($root && $root._listeners?.has(eventType)) {
$root.removeEventListener(eventType, handleEvent, true);
$root._listeners.delete(eventType);
}
}
}
1 change: 1 addition & 0 deletions src/lib/renderElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function renderElement(vNode, container) {
// 최초 렌더링시에는 createElement로 DOM을 생성하고
// 이후에는 updateElement로 기존 DOM을 업데이트한다.
// 렌더링이 완료되면 container에 이벤트를 등록한다.
console.log(`renderElement ${JSON.stringify(vNode)}, ${container}`);

const oldNode = OldNodeMap.get(container);
const newNode = normalizeVNode(vNode);
Expand Down
54 changes: 27 additions & 27 deletions src/lib/updateElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ function updateAttributes(target, originNewProps, originOldProps) {

for (const attr in oldProps) {
if (!(attr in newProps)) {
if (attr.startsWith("on") && typeof oldProps[attr] === "function") {
const eventType = attr.toLowerCase().slice(2);
if (attr.startsWith("on") && typeof newProps[attr] === "function") {
const eventType = attr.slice(2).toLowerCase();
removeEvent(target, eventType, oldProps[attr]);
} else {
target.removeAttribute(attr);
Expand All @@ -19,63 +19,63 @@ function updateAttributes(target, originNewProps, originOldProps) {
for (const attr in newProps) {
if (oldProps[attr] !== newProps[attr]) {
if (attr.startsWith("on") && typeof newProps[attr] === "function") {
const eventType = attr.toLowerCase().slice(2);
const eventType = attr.slice(2).toLowerCase();
if (typeof oldProps[attr] === "function") {
removeEvent(target, eventType, oldProps[attr]);
}
addEvent(target, eventType, newProps[attr]);
} else if (attr === "className") {
target.className = newProps[attr];
} else if (attr === "style" && typeof newProps[attr] === "object") {
Object.entries(newProps[attr]).forEach(([key, value]) => {
target.style[key] = value;
});
} else {
target.setAttribute(attr, newProps[attr]);
}
}
}
}

export function updateElement(parentElement, newNode, oldNode, index = 0) {
const existingNode = parentElement.childNodes[index];

// oldNode만 있는 경우: oldNode를 parentElement에서 제거한다.
if (!newNode && oldNode) {
return parentElement.removeChild(parentElement.childNodes[index]);
parentElement.removeChild(existingNode);
return;
}

// newNode만 있는 경우: newNode를 parentElement에 추가한다.
if (!oldNode && newNode) {
return parentElement.appendChild(createElement(newNode));
parentElement.appendChild(createElement(newNode));
return;
}

// oldNode와 newNode 모두 string인 경우: oldNode와 newNode 내용이 다르다면, newNode 내용으로 교체한다.
if (typeof oldNode === "string" || typeof newNode === "string") {
if (newNode === oldNode) return;
return parentElement.replaceChild(
createElement(newNode),
parentElement.childNodes[index],
);
if (newNode !== oldNode) {
parentElement.replaceChild(createElement(newNode), existingNode);
}
return;
}

// oldeNode와 newNode의 태그 이름(type)이 다를 경우: oldNode를 제거하고 해당 위치에 newNode를 추가한다.
if (oldNode.type !== newNode.type) {
return parentElement.replaceChild(
createElement(newNode),
parentElement.childNodes[index],
);
parentElement.replaceChild(createElement(newNode), existingNode);
return;
}

// oldNode와 newNode의 태그 이름(type)이 같을 경우: newNode와 oldNode의 속성을 비교하여 변경된 부분만 반영한다.
if (oldNode.type === newNode.type) {
updateAttributes(
parentElement.childNodes[index],
newNode.props || {},
oldNode.props || {},
);
}
updateAttributes(existingNode, newNode?.props, oldNode?.props);

// oldNode와 newNode를 순회하며, 앞에 조건식을 반복한다.
const maxLength = Math.max(newNode.children.length, oldNode.children.length);
const newChildren = newNode.children || [];
const oldChildren = oldNode.children || [];

const maxLength = Math.max(newChildren.length, oldChildren.length);

for (let i = 0; i < maxLength; i++) {
updateElement(
parentElement.childNodes[index],
newNode.children[i],
oldNode.children[i],
);
updateElement(existingNode, newChildren[i], oldChildren[i], i);
}
}

0 comments on commit c816674

Please sign in to comment.