diff --git a/assets/creator-studio/src/editor-content-style.ts b/assets/creator-studio/src/editor-content-style.ts index 18f73dbc..e3b32b90 100644 --- a/assets/creator-studio/src/editor-content-style.ts +++ b/assets/creator-studio/src/editor-content-style.ts @@ -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; } diff --git a/assets/css/frontend/pages/submit-editor.css b/assets/css/frontend/pages/submit-editor.css index d0e80090..5eb1a768 100644 --- a/assets/css/frontend/pages/submit-editor.css +++ b/assets/css/frontend/pages/submit-editor.css @@ -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%; } diff --git a/assets/js/front-submit-editor-basics.js b/assets/js/front-submit-editor-basics.js index 04882782..c2fa2d81 100644 --- a/assets/js/front-submit-editor-basics.js +++ b/assets/js/front-submit-editor-basics.js @@ -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 渲染提示文字。 */ diff --git a/tests/widget-system/creator-studio-contract.test.cjs b/tests/widget-system/creator-studio-contract.test.cjs index 0c1b1f54..e0b26208 100644 --- a/tests/widget-system/creator-studio-contract.test.cjs +++ b/tests/widget-system/creator-studio-contract.test.cjs @@ -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\)/); diff --git a/tests/widget-system/submit-placeholder-behavior.test.cjs b/tests/widget-system/submit-placeholder-behavior.test.cjs index 9c6bbd21..1d1e85b6 100644 --- a/tests/widget-system/submit-placeholder-behavior.test.cjs +++ b/tests/widget-system/submit-placeholder-behavior.test.cjs @@ -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( + '