Created
August 17, 2023 08:22
-
-
Save MicroCBer/a412a6c27a6dc0e49c091ce64e96ae6b to your computer and use it in GitHub Desktop.
Create an element delegate/proxy in js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const createElementDelegate = (ele, config) => { | |
const old2NewMap = new WeakMap(), new2OldMap = new WeakMap(); | |
const clone = (ele, {cloneParent, syncProps, delegateEvents=true, delayedElementListChange}={}) => { | |
if(ele == null) return ele; | |
// clone, and delegate events, and preserve parent classes | |
let cloned = ele.cloneNode(); | |
old2NewMap.set(ele, cloned); | |
new2OldMap.set(cloned, ele); | |
// clone child also | |
for(const child of ele.childNodes) | |
cloned.appendChild(clone(child)); | |
// event delegate | |
if(delegateEvents) | |
for(const prop in ele){ | |
if(prop.startsWith('on')){ | |
cloned[prop] = e => ele.dispatchEvent(new e.constructor(e.type,e)) | |
} | |
} | |
if(cloneParent) { | |
// wrap in fake parents | |
let parent = ele; | |
while(parent=parent.parentElement){ | |
if(parent.tagName==='HTML') break; | |
const clonedParent = parent.cloneNode(); | |
clonedParent.appendChild(cloned); | |
cloned = clonedParent; | |
} | |
} | |
if(syncProps) { | |
new MutationObserver((records)=>{ | |
for(const record of records) { | |
const clonedTarget = old2NewMap.get(record.target); | |
const target = record.target; | |
if(record.type === 'attributes') { | |
const attr = record.attributeName; | |
clonedTarget.setAttribute(attr,target.getAttribute(attr)); | |
} | |
else if(record.type === 'childList') { | |
const process = ()=>{ | |
for(const added of record.addedNodes) { | |
if(old2NewMap.get(record.nextSibling)) | |
clonedTarget.insertBefore(clone(added), old2NewMap.get(record.nextSibling)); | |
else | |
clonedTarget.appendChild(clone(added)); | |
} | |
for(const removed of record.removedNodes) { | |
const clonedRemoved = old2NewMap.get(removed); | |
if(clonedRemoved) { | |
clonedRemoved.remove(); | |
} | |
} | |
}; | |
if(delayedElementListChange) setTimeout(process); | |
else process(); | |
} | |
} | |
}).observe(ele, {attributes: true, childList: true, subtree: true}) | |
} | |
return cloned; | |
} | |
return clone(ele, config); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// run in any stackoverflow question pages with answers. | |
// this would create a perfect delegate dom of answers element, all the buttons should be interactable. | |
// sadly, keyboard input is not working | |
const win = window.open('about:blank', undefined, 'popup'); | |
for(const style of document.querySelectorAll('style, link')) | |
win.document.head.appendChild(style.cloneNode(true)); | |
win.document.body.appendChild(createElementDelegate(document.querySelector("#answers"), {cloneParent:true, syncProps: true})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment