动态 contenteditable 脚本在 Firefox 上崩溃(但 运行 在 Chrome 上)
Dynamic contenteditable script crashing on Firefox (but running on Chrome)
我正在尝试使用 contenteditable div 的动态克隆来模拟多页编辑器。 运行 在 Chrome 上非常流畅,但在 Firefox 上,崩溃:
我什至无法调试在 Firefox 上崩溃的问题。
我该怎么做才能解决这个问题?
提前致谢!
我在 Firefox 上试过这个例子,当我到达一个新页面时它崩溃了。这背后的原因是,pages
HTMLCollection 在 for 循环中被操作。这类似于 this 问题。
正如 this 所建议的那样,如果您改为使用向后迭代,则可以避免该问题。然而,最好的解决方案是根本不使用迭代。您可以在创建新页面时动态附加事件侦听器,并且可以访问该页面的所有兄弟页面。
此外,在您的示例中,新行在 Chrome 和 Firefox 上的显示方式不同。 Chrome 为每一行添加了一个新的 <div>
,Firefox 使用的是 <br>
。将 DefaultParagraphSeparator
更改为 div
会在两个浏览器上产生相同的行。
此示例适用于两种浏览器:
function redator(divId) {
const root = document.getElementById(divId)
const a4 = {
height: 830,
width: 595,
lineHeight: 30
};
const getChildrenHeight = (element) => {
total = 0;
if (element.childNodes) {
for (let child of element.childNodes) {
switch (child.nodeType) {
case Node.ELEMENT_NODE:
total += child.offsetHeight;
break;
case Node.TEXT_NODE:
let range = document.createRange();
range.selectNodeContents(child);
rect = range.getBoundingClientRect();
total += (rect.bottom - rect.top);
break;
}
}
}
return total;
};
const setSelection = (node, offset) => {
let range = document.createRange();
let sel = window.getSelection();
range.setStart(node, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
const addPage = () => {
const firstPage = root.querySelector('#page-1');
const newPage = firstPage.cloneNode(true);
const pages = root.getElementsByClassName('page');
newPage.addEventListener('input', onInput);
newPage.innerHTML = '';
newPage.id = 'page-' + (pages.length + 1);
firstPage.parentNode.appendChild(newPage);
newPage.focus();
newPage._emptyPage = true;
return newPage;
}
function onInput(e) {
const page = this;
const previousPage = page.previousElementSibling;
const nextPage = page.nextElementSibling;
const pageHeight = getChildrenHeight(page);
const lastChild = page.lastChild;
const cloneChild = lastChild.cloneNode(true);
const textContent = page.innerText;
if ((pageHeight === 0 || textContent.length <= 1) && !!previousPage && !page._emptyPage) {
page.remove();
previousPage.focus();
const lastChild = previousPage.lastChild;
setSelection(lastChild, lastChild.childNodes.length);
} else if (pageHeight > a4.height && !nextPage) {
lastChild.remove();
addPage().appendChild(cloneChild);
} else if (pageHeight > a4.height && nextPage) {
lastChild.remove();
nextPage.insertBefore(cloneChild, nextPage.firstChild);
let selection = getSelection().getRangeAt(0).startContainer.parentElement.closest('div');
if(selection === page.lastChild) {
setSelection(cloneChild, 0);
}
} else if (pageHeight < a4.height - a4.lineHeight && !!nextPage) {
let firstChildOfNextPage = nextPage.firstChild;
let clone = firstChildOfNextPage.cloneNode(true);
firstChildOfNextPage.remove();
page.appendChild(clone);
}
page._emptyPage = false;
}
document.execCommand("DefaultParagraphSeparator", false, "div");
root.querySelector('#page-1').addEventListener('input', onInput);
}
redator('editor');
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
#editor {
background-color: gray;
border: 1px black;
padding: 1em 2em;
}
.page {
background-color: white;
border: solid black;
padding: 1em 2em;
width: 595px;
height: 841px;
word-wrap: break-word;
overflow-wrap: break-word;
white-space: normal;
}
</style>
</head>
<body>
<h3>My Editor</h3>
<div id="editor">
<div contenteditable="true" class="page" id="page-1">
<b>hello</b>
</div>
</div>
</body>
</html>
我正在尝试使用 contenteditable div 的动态克隆来模拟多页编辑器。 运行 在 Chrome 上非常流畅,但在 Firefox 上,崩溃:
我什至无法调试在 Firefox 上崩溃的问题。 我该怎么做才能解决这个问题?
提前致谢!
我在 Firefox 上试过这个例子,当我到达一个新页面时它崩溃了。这背后的原因是,pages
HTMLCollection 在 for 循环中被操作。这类似于 this 问题。
正如 this 所建议的那样,如果您改为使用向后迭代,则可以避免该问题。然而,最好的解决方案是根本不使用迭代。您可以在创建新页面时动态附加事件侦听器,并且可以访问该页面的所有兄弟页面。
此外,在您的示例中,新行在 Chrome 和 Firefox 上的显示方式不同。 Chrome 为每一行添加了一个新的 <div>
,Firefox 使用的是 <br>
。将 DefaultParagraphSeparator
更改为 div
会在两个浏览器上产生相同的行。
此示例适用于两种浏览器:
function redator(divId) {
const root = document.getElementById(divId)
const a4 = {
height: 830,
width: 595,
lineHeight: 30
};
const getChildrenHeight = (element) => {
total = 0;
if (element.childNodes) {
for (let child of element.childNodes) {
switch (child.nodeType) {
case Node.ELEMENT_NODE:
total += child.offsetHeight;
break;
case Node.TEXT_NODE:
let range = document.createRange();
range.selectNodeContents(child);
rect = range.getBoundingClientRect();
total += (rect.bottom - rect.top);
break;
}
}
}
return total;
};
const setSelection = (node, offset) => {
let range = document.createRange();
let sel = window.getSelection();
range.setStart(node, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
const addPage = () => {
const firstPage = root.querySelector('#page-1');
const newPage = firstPage.cloneNode(true);
const pages = root.getElementsByClassName('page');
newPage.addEventListener('input', onInput);
newPage.innerHTML = '';
newPage.id = 'page-' + (pages.length + 1);
firstPage.parentNode.appendChild(newPage);
newPage.focus();
newPage._emptyPage = true;
return newPage;
}
function onInput(e) {
const page = this;
const previousPage = page.previousElementSibling;
const nextPage = page.nextElementSibling;
const pageHeight = getChildrenHeight(page);
const lastChild = page.lastChild;
const cloneChild = lastChild.cloneNode(true);
const textContent = page.innerText;
if ((pageHeight === 0 || textContent.length <= 1) && !!previousPage && !page._emptyPage) {
page.remove();
previousPage.focus();
const lastChild = previousPage.lastChild;
setSelection(lastChild, lastChild.childNodes.length);
} else if (pageHeight > a4.height && !nextPage) {
lastChild.remove();
addPage().appendChild(cloneChild);
} else if (pageHeight > a4.height && nextPage) {
lastChild.remove();
nextPage.insertBefore(cloneChild, nextPage.firstChild);
let selection = getSelection().getRangeAt(0).startContainer.parentElement.closest('div');
if(selection === page.lastChild) {
setSelection(cloneChild, 0);
}
} else if (pageHeight < a4.height - a4.lineHeight && !!nextPage) {
let firstChildOfNextPage = nextPage.firstChild;
let clone = firstChildOfNextPage.cloneNode(true);
firstChildOfNextPage.remove();
page.appendChild(clone);
}
page._emptyPage = false;
}
document.execCommand("DefaultParagraphSeparator", false, "div");
root.querySelector('#page-1').addEventListener('input', onInput);
}
redator('editor');
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
#editor {
background-color: gray;
border: 1px black;
padding: 1em 2em;
}
.page {
background-color: white;
border: solid black;
padding: 1em 2em;
width: 595px;
height: 841px;
word-wrap: break-word;
overflow-wrap: break-word;
white-space: normal;
}
</style>
</head>
<body>
<h3>My Editor</h3>
<div id="editor">
<div contenteditable="true" class="page" id="page-1">
<b>hello</b>
</div>
</div>
</body>
</html>