fix(submit): detect empty body without editor chrome and let body fill the frame
登录态实测(Edge + 真实 TinyMCE)复现了上一版没修好的两点:
1. 占位符仍不显示:editor-runtime 会把块级删除浮层插进 body,它带
data-mce-bogus 但含「✕ 选中此块」文字,body.textContent 因此永远非空,
data-jio-empty 一直没被打上。判断正文是否为空时先剔除 data-mce-bogus 节点。
2. 点空白落不到正文:body 高度只有 29px。`min-height:100%` 的父级 html
没有确定高度,百分比退化成 auto。改为 html{height:100%} +
body{min-height:100%},body 现在铺满 520px 的 iframe。
实测结果(登录态 https://localhost/submit/):
- 首次加载 data-jio-empty=1、::before 渲染「请输入正文」;
- 点击正文中部空白,光标落进 P 并可继续输入;
- 引用块内编辑后点空白,光标落到引用之后的段落;
- 清空内容后占位符恢复。
测试:新增「编辑器辅助节点不算正文」回归用例;900 → 901 全通过。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5afa7ab9df
commit
305896796c
5 files changed
+52
-10
No files matched your search
@@ -39,14 +39,17 @@ body[data-jio-empty="1"]::before {
|
||||
/*
|
||||
* body 撑满编辑区:内容很短时点击正文下方的空白区域也应落光标。
|
||||
* iframe 高度由 syncEditorHeight() 跟随内容,这里保证 body 至少铺满。
|
||||
*
|
||||
* min-height:100% 依赖父级有确定高度,所以 html 必须 height:100%
|
||||
* (只写 min-height 的话 html 的高度仍由内容决定,百分比会退化成 auto)。
|
||||
*/
|
||||
html,
|
||||
body {
|
||||
min-height: 100%;
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 { margin: 1.5em 0 0.65em; line-height: 1.4; }
|
||||
|
||||
@@ -227,13 +227,16 @@ body[data-jio-empty="1"]::before {
|
||||
/*
|
||||
* body 撑满编辑区:内容很短时点击正文下方的空白区域也应落光标,
|
||||
* 否则只有文字所在的那一行能点。
|
||||
*
|
||||
* min-height:100% 依赖父级有确定高度,所以 html 必须 height:100%(只写
|
||||
* min-height 的话 html 的高度仍由内容决定,百分比会退化成 auto)。
|
||||
*/
|
||||
html,
|
||||
body.mce-content-body {
|
||||
min-height: 100%;
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body.mce-content-body {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
min-height: 100%;
|
||||
}
|
||||
@@ -40,16 +40,32 @@
|
||||
return editor.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 正文自身的副本:剔除 TinyMCE 的辅助节点。
|
||||
*
|
||||
* 编辑器会把块级删除浮层(#jio-block-actions)之类带文字的工具节点插进
|
||||
* body,它们都带 data-mce-bogus。直接读 body.textContent 会把「选中此块」
|
||||
* 当成正文,导致空正文永远判成非空、占位符不显示。
|
||||
*/
|
||||
function contentClone(body) {
|
||||
var clone = body.cloneNode(true);
|
||||
Array.prototype.forEach.call(clone.querySelectorAll('[data-mce-bogus]'), function (node) {
|
||||
node.parentNode && node.parentNode.removeChild(node);
|
||||
});
|
||||
return clone;
|
||||
}
|
||||
|
||||
function isEmpty() {
|
||||
var body = root();
|
||||
if (!body) {
|
||||
return false;
|
||||
}
|
||||
var content = contentClone(body);
|
||||
// 图片 / 视频 / 表格这类块即使没有文字也算有内容。
|
||||
if (body.querySelector(MEDIA)) {
|
||||
if (content.querySelector(MEDIA)) {
|
||||
return false;
|
||||
}
|
||||
return String(body.textContent || '').replace(/[\s ]+/g, '') === '';
|
||||
return String(content.textContent || '').replace(/[\s ]+/g, '') === '';
|
||||
}
|
||||
|
||||
/** 空正文标记交给 CSS 渲染提示文字。 */
|
||||
|
||||
@@ -412,8 +412,11 @@ test('submit editor renders the configured body placeholder', () => {
|
||||
assert.match(styles, /body\[data-jio-empty="1"\]::before\s*\{/);
|
||||
assert.match(contentStyle, /body\[data-jio-empty="1"\]::before\s*\{/);
|
||||
// 两份 content CSS 都要让 body 撑满,否则正文下方的空白不属于 iframe。
|
||||
assert.match(styles, /html,\s*\n\s*body\.mce-content-body\s*\{[^}]*min-height:\s*100%/);
|
||||
assert.match(contentStyle, /html,\s*\n\s*body\s*\{[^}]*min-height:\s*100%/);
|
||||
// min-height:100% 依赖父级确定高度,html 必须 height:100%。
|
||||
assert.match(styles, /html\s*\{[^}]*height:\s*100%/);
|
||||
assert.match(styles, /body\.mce-content-body\s*\{[^}]*min-height:\s*100%/);
|
||||
assert.match(contentStyle, /html\s*\{[^}]*height:\s*100%/);
|
||||
assert.match(contentStyle, /body\s*\{[^}]*min-height:\s*100%/);
|
||||
|
||||
// 空正文时 iframe 不能缩到只剩一行:高度下限取编辑器声明的高度。
|
||||
assert.match(runtime, /getParam\?\.\('height', 0\)/);
|
||||
|
||||
@@ -139,6 +139,23 @@ test('placeholder never becomes a DOM node in the editable body', () => {
|
||||
assert.equal(doc.body.textContent.trim(), '');
|
||||
});
|
||||
|
||||
test('editor chrome injected into the body does not count as content', () => {
|
||||
// 真实页面上 editor-runtime 会把块删除浮层插进 body,它带 data-mce-bogus
|
||||
// 但含文字(「✕ 选中此块」)。若按 body.textContent 判断,空正文永远非空,
|
||||
// 占位符就永远不显示 —— 这正是「首次加载不显示」的真实原因。
|
||||
const { doc, editor } = createEditor(
|
||||
'<p><br data-mce-bogus="1"></p>' +
|
||||
'<div id="jio-block-actions" data-mce-bogus="all">' +
|
||||
'<button type="button"><span>✕</span><span>选中此块</span></button></div>',
|
||||
{ placeholder: '请输入正文' }
|
||||
);
|
||||
const plugin = loadPlugin()[PLUGIN];
|
||||
plugin(editor);
|
||||
editor.fire('init');
|
||||
|
||||
assert.equal(doc.body.getAttribute('data-jio-empty'), '1', 'bogus chrome must not count as content');
|
||||
});
|
||||
|
||||
test('no placeholder configured leaves the body untouched', () => {
|
||||
const { doc, editor } = createEditor('<p><br data-mce-bogus="1"></p>', {});
|
||||
const plugin = loadPlugin()[PLUGIN];
|
||||
|
||||
Reference in new issue
Block a user