问题:仅首个窗口可拖拽,如何让新增窗口支持拖拽?
解决可拖拽弹窗复制后无交互功能的问题
问题根源
- DOM元素克隆(
cloneNode(true))不会复制通过addEventListener绑定的事件监听器,新生成的窗口自然没有拖拽/缩放功能 - 全局的拖拽、缩放函数硬绑定到初始窗口元素,新窗口调用时仍会操作原窗口而非自身
closeWindow函数固定选择#window,无法正确处理非初始窗口的关闭逻辑
修正方案:用类封装窗口行为
将每个窗口的状态、拖拽、缩放、关闭逻辑封装成独立类,确保每个新窗口实例自动拥有完整交互功能:
HTML
<div data-aos="zoom-in" data-aos-delay="300" id="parent"> <!-- 初始窗口将通过JS创建 --> </div>
CSS
#parent { position: relative; width: 800px; height: 600px; border: 2px solid orange; } .old-computer-window { position: absolute; width: 300px; max-height: 1080px; background-color: white; border-style: solid; border-width: 2px; border-left-color: #191919; border-top-color: #191919; border-bottom-color: darkred; border-right-color: darkred; user-select: none; overflow: hidden; } .header2 { background-color: black; color: #fff; padding: 10px; display: flex; justify-content: space-between; align-items: center; cursor: move; } .close { cursor: pointer; padding: 0 8px; border: 2px solid; border-left-color: white; border-top-color: white; border-bottom-color: darkred; border-right-color: darkred; } .resize-container { position: absolute; width: 15px; height: 15px; right: 0; bottom: 0; cursor: se-resize; background-color: rgba(0,0,0,0.1); } .image-container { height: calc(100% - 40px); background-color: #eee; }
JavaScript
const parentElement = document.querySelector("#parent"); const minimumWidth = 150; const minimumHeight = 150; class WindowInstance { constructor() { this.element = this.createWindowElement(); this.isDragging = false; this.isResizing = false; this.xOffset = 0; this.yOffset = 0; this.bindEvents(); this.placeRandomly(); parentElement.appendChild(this.element); } createWindowElement() { const windowEl = document.createElement("div"); windowEl.classList.add("old-computer-window"); const header = document.createElement("div"); header.classList.add("header2"); header.innerHTML = `<p>DANIEL.JPG</p><div class="close">×</div>`; const imageContainer = document.createElement("div"); imageContainer.classList.add("image-container"); const resizeContainer = document.createElement("div"); resizeContainer.classList.add("resize-container"); windowEl.appendChild(header); windowEl.appendChild(imageContainer); windowEl.appendChild(resizeContainer); return windowEl; } bindEvents() { const header = this.element.querySelector(".header2"); const resizeContainer = this.element.querySelector(".resize-container"); const closeBtn = this.element.querySelector(".close"); // 拖拽事件 header.addEventListener("mousedown", (e) => this.dragStart(e)); document.addEventListener("mouseup", () => this.dragEnd()); document.addEventListener("mousemove", (e) => this.drag(e)); // 缩放事件 resizeContainer.addEventListener("mousedown", (e) => this.resizeStart(e)); document.addEventListener("mouseup", () => this.resizeEnd()); document.addEventListener("mousemove", (e) => this.resize(e)); // 关闭事件 closeBtn.addEventListener("click", () => this.closeWindow()); } dragStart(e) { this.initialX = e.clientX - this.xOffset; this.initialY = e.clientY - this.yOffset; this.isDragging = true; } dragEnd() { this.isDragging = false; } drag(e) { if (!this.isDragging) return; e.preventDefault(); this.currentX = e.clientX - this.initialX; this.currentY = e.clientY - this.initialY; this.xOffset = this.currentX; this.yOffset = this.currentY; this.setTranslate(this.currentX, this.currentY); } setTranslate(xPos, yPos) { this.element.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`; } resizeStart(e) { this.initialX = e.clientX; this.initialY = e.clientY; this.initialWidth = this.element.offsetWidth; this.initialHeight = this.element.offsetHeight; this.isResizing = true; } resizeEnd() { this.isResizing = false; } resize(e) { if (!this.isResizing) return; const currentWidth = this.initialWidth + (e.clientX - this.initialX); const currentHeight = this.initialHeight + (e.clientY - this.initialY); if (currentWidth >= minimumWidth) { this.element.style.width = `${currentWidth}px`; } if (currentHeight >= minimumHeight) { this.element.style.height = `${currentHeight}px`; } } placeRandomly() { const xPos = Math.floor(Math.random() * (parentElement.offsetWidth - this.element.offsetWidth)); const yPos = Math.floor(Math.random() * (parentElement.offsetHeight - this.element.offsetHeight)); this.setTranslate(xPos, yPos); } closeWindow() { this.element.style.display = "none"; setTimeout(() => { // 生成两个新窗口实例 new WindowInstance(); new WindowInstance(); // 移除当前窗口元素 this.element.remove(); }, 500); } } // 创建初始窗口 new WindowInstance();
关键改进点
- 每个窗口实例拥有独立的状态变量(如
isDragging、xOffset),避免全局变量冲突 - 事件监听器直接绑定到实例方法,确保操作的是当前窗口元素
- 关闭逻辑自动生成新的窗口实例,新窗口会自动初始化所有交互事件
- 使用
document级别的mouseup和mousemove事件,避免鼠标移出窗口时交互中断
内容的提问来源于stack exchange,提问作者No Penta No Problem
相关产品推荐
相关产品推荐

