使用 Javascript 和可编辑文档创建内容助手(ctrl+space)

发布于 2024-12-13 20:53:06 字数 2425 浏览 1 评论 0原文

我在 html 文档的可编辑区域中创建了内容助手的运行示例。因此,如果用户按下键盘上的 ctrl 和空格键,则会出现上下文菜单。目前(参见下面的演示)上下文菜单位于右侧 y 位置(文本下方)。但它不沿着 x 轴(如果文本变长,框将播种在行的开头)。

你能帮我解决这个问题吗?

问候, 示例

代码:

<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Test</title>
<script type="text/javascript">

var iframe = null, iwindow = null, iDocument = null;

function setUpInput() {
        iframe = document.createElement( 'iframe' );
        iframe.setAttribute( 'id', 'iframe-test' );
        iframe.setAttribute( 'frameborder', 0 );
        iframe.setAttribute( 'style', 'width:100%; height:100%;border: solid 1px red;' );

        document.getElementById( "input" ).appendChild( iframe );

        iwindow   = iframe.contentWindow;
        idocument = iwindow.document;

        idocument.open();
        idocument.write("<p></p>");
        idocument.close();

        idocument.body.setAttribute( 'spellcheck', false );
        idocument.body.setAttribute( 'style', 'font-family: Consolas,serif;font-size: 0.8em;' );
        idocument.body.contentEditable = true;

        iwindow.onkeydown = function(e) {
            if (e.ctrlKey && e.keyCode == 32) {
                createSuggestObject();
                return false;
            }
            if (e.ctrlKey) return false;
        };

        iwindow.onkeypress = function(e) {if (e.ctrlKey) return false;};
}

function createSuggestObject() {
suggest = new Object();
suggest.box = document.createElement( 'div' );
suggest.box.style.position = 'absolute';
suggest.box.style.width = '120px';
suggest.box.style.overflow = 'auto';
suggest.box.style.border = '1px solid #BEC7E4';
suggest.box.style.display = 'block';
suggest.box.style.marginTop = '16px';
suggest.box.innerHTML = "Example 1";
document.body.appendChild( suggest.box )

var position = iframe.getBoundingClientRect();

var selObj = iwindow.getSelection();
var selRange = selObj.getRangeAt(0);
var p2 = selObj.anchorNode.parentNode.getBoundingClientRect();  

suggest.box.style.top = Math.round( window.scrollY + position.top + p2.top) + 'px';
suggest.box.style.left = Math.round( window.scrollX + position.left + p2.left) + 'px';

}

window.onload = function() {
    setUpInput();
};
</script>

</head>
<body>
<div id="input"></div>
</body>
</html>

I've created a running example of a content assistant in an editable area on a html document. So if the user hits ctrl and space on the keyboard a context menu appears. Currently (see demo bellow) the context menu is on the right y position (bellow the text). But it goes not along with the x-axis (if the text becomes longer the box will be sown on the beginning of the line).

Can you help me solving this problem?

Greetings,
mythbu

Example code:

<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Test</title>
<script type="text/javascript">

var iframe = null, iwindow = null, iDocument = null;

function setUpInput() {
        iframe = document.createElement( 'iframe' );
        iframe.setAttribute( 'id', 'iframe-test' );
        iframe.setAttribute( 'frameborder', 0 );
        iframe.setAttribute( 'style', 'width:100%; height:100%;border: solid 1px red;' );

        document.getElementById( "input" ).appendChild( iframe );

        iwindow   = iframe.contentWindow;
        idocument = iwindow.document;

        idocument.open();
        idocument.write("<p></p>");
        idocument.close();

        idocument.body.setAttribute( 'spellcheck', false );
        idocument.body.setAttribute( 'style', 'font-family: Consolas,serif;font-size: 0.8em;' );
        idocument.body.contentEditable = true;

        iwindow.onkeydown = function(e) {
            if (e.ctrlKey && e.keyCode == 32) {
                createSuggestObject();
                return false;
            }
            if (e.ctrlKey) return false;
        };

        iwindow.onkeypress = function(e) {if (e.ctrlKey) return false;};
}

function createSuggestObject() {
suggest = new Object();
suggest.box = document.createElement( 'div' );
suggest.box.style.position = 'absolute';
suggest.box.style.width = '120px';
suggest.box.style.overflow = 'auto';
suggest.box.style.border = '1px solid #BEC7E4';
suggest.box.style.display = 'block';
suggest.box.style.marginTop = '16px';
suggest.box.innerHTML = "Example 1";
document.body.appendChild( suggest.box )

var position = iframe.getBoundingClientRect();

var selObj = iwindow.getSelection();
var selRange = selObj.getRangeAt(0);
var p2 = selObj.anchorNode.parentNode.getBoundingClientRect();  

suggest.box.style.top = Math.round( window.scrollY + position.top + p2.top) + 'px';
suggest.box.style.left = Math.round( window.scrollX + position.left + p2.left) + 'px';

}

window.onload = function() {
    setUpInput();
};
</script>

</head>
<body>
<div id="input"></div>
</body>
</html>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

走野 2024-12-20 20:53:06

您的解决方案的主要问题是您使用 p 元素的边界矩形(您假设它包含用户输入的文本)作为放置建议对象的位置的引用。

首先,您没有将边界矩形宽度考虑到建议对象的位置,因此无论文本有多长,您的建议框都位于左侧。

然而,这种方法最终会失败,因为如果文本有多于一行(其中第二行比第一行短),边界矩形的宽度将等于第一行的长度(或多或少)。因此,建议对象的位置将不正确。

我关于如何修复代码的第一个想法是将一些内联元素附加到文本(信标,如果你愿意的话),测量它的位置,将其从 DOM 中删除并使用计算出的位置来正确设置建议对象。

结果几乎成功了。几乎,因为事实证明,不同的浏览器使用不同的 处理内容可编辑的行结尾。例如,Firefox 会在行尾插入
,而 Chrome 则不会。因此,当我尝试在文本后面附加我的 beacon 元素时,它会附加在
之后,再次导致位置错误。

解决方案是改变获取我们正在处理的文本节点的方式,并在它的 nextSibling 之前插入 beacon

这是工作示例 http://jsfiddle.net/MmKXS/10/

注 1:I已删除在 iframe 文档中添加空

元素,因为在 Chrome 中,用户输入的文本未插入其中,从而导致另一个问题带有定位建议对象。

注 2:目前我的解决方案仅适用于将建议对象定位在文本末尾,不适用于光标位置,因为这会涉及拆分textNodes,插入信标,检查其位置并再次合并textNodes。根据该代码的用例,可能会导致性能不佳和/或可能需要更改处理建议对象定位的整个方法。

Main problem with your solution was that you were using bounding rectangle of the p element (which as you assumed contained text inputed by user) as a reference to where to place suggest object.

First of all you didn't account bounding rectangle width into the position of the suggest object, so your suggestion box stayed on the left no matter how long the text was.

However, that approach would fail eventually because if the text would have more than one line (where the second line would be shorter than the first one), the width of bounding rectangle would equal length of the first line (more or less). Hence, the suggestion object would be positioned incorrectly.

My first idea of how to fix your code was to append some inline element to the text (beacon, if you will), measure it's position, remove it from the DOM and use that calculated position to properly set suggestion object.

It turned out to almost work. Almost, because as it turned out, different browsers use different methods of dealing with contentEditable line endings. Firefox for example inserts <br _moz_dirty=""/> at the end of the line, while Chrome does not. So when I tried to append my beacon element after the text it was appended after that <br/> causing incorrect position again.

Solution was to change the way of getting text node we're dealing with and insert beacon right before nextSibling of it.

Here's working example http://jsfiddle.net/MmKXS/10/

Note 1: I've removed addition of empty <p></p> element to the document of the iframe since in Chrome text inputed by user wasn't inserted into it, causing yet another problems with positioning suggest object.

Note 2: As for now my solution only works for positioning suggestion object at the end of the text, not at the cursor position as it would involve splitting textNodes, inserting the beacon, checking its position and merging textNodes again. Depending on use case you have for that code that could lead to poor performance and/or could require changing the whole approach of how to deal with positioning your suggestion object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文