获取选定的文本位置并在其旁边放置一个元素

IT技术 javascript dom
2021-02-03 23:32:54

tl;博士

这个想法是允许用户标记任何文本并看到选择旁边的菜单弹出窗口,可能的操作应用于所选文本。


我需要在用户选择的文本旁边放置一个绝对定位按钮。

我正在将mouseup 事件绑定到 Document,并获取选定的文本,但我目前不知道如何知道实际选择的位置,而不将其包装在某个元素中,因为文本的选择可以跨越几个元素,如果我将它包装起来,它会弄乱结构。

3个回答

您可以在选择的末尾放置一个标记跨度,使用 jQuery 获取其坐标,将您的按钮放在这些坐标上并删除标记跨度。

以下应该让你开始:

var markSelection = (function() {
    var markerTextChar = "\ufeff";
    var markerTextCharEntity = "";

    var markerEl, markerId = "sel_" + new Date().getTime() + "_" + Math.random().toString().substr(2);

    var selectionEl;

    return function(win) {
        win = win || window;
        var doc = win.document;
        var sel, range;
        // Branch for IE <= 8 
        if (doc.selection && doc.selection.createRange) {
            // Clone the TextRange and collapse
            range = doc.selection.createRange().duplicate();
            range.collapse(false);

            // Create the marker element containing a single invisible character by creating literal HTML and insert it
            range.pasteHTML('<span id="' + markerId + '" style="position: relative;">' + markerTextCharEntity + '</span>');
            markerEl = doc.getElementById(markerId);
        } else if (win.getSelection) {
            sel = win.getSelection();
            range = sel.getRangeAt(0).cloneRange();
            range.collapse(false);

            // Create the marker element containing a single invisible character using DOM methods and insert it
            markerEl = doc.createElement("span");
            markerEl.id = markerId;
            markerEl.appendChild( doc.createTextNode(markerTextChar) );
            range.insertNode(markerEl);
        }

        if (markerEl) {
            // Lazily create element to be placed next to the selection
            if (!selectionEl) {
                selectionEl = doc.createElement("div");
                selectionEl.style.border = "solid darkblue 1px";
                selectionEl.style.backgroundColor = "lightgoldenrodyellow";
                selectionEl.innerHTML = "&lt;- selection";
                selectionEl.style.position = "absolute";

                doc.body.appendChild(selectionEl);
            }

            // Find markerEl position http://www.quirksmode.org/js/findpos.html
        var obj = markerEl;
        var left = 0, top = 0;
        do {
            left += obj.offsetLeft;
            top += obj.offsetTop;
        } while (obj = obj.offsetParent);

            // Move the button into place.
            // Substitute your jQuery stuff in here
            selectionEl.style.left = left + "px";
            selectionEl.style.top = top + "px";

            markerEl.parentNode.removeChild(markerEl);
        }
    };
})();
@Rick:没错。getBoundingClientRect()在主要浏览器中将相对于视口的位置(这是返回的位置)转换为相对于整个文档的位置所需的代码非常复杂,我不想在这里添加它。
2021-03-18 23:32:54
这段代码不会给出错误:undefined has no property named setStart对于使用旧 webkit 的浏览器?在第 26 行有一个缺失的 range 变量赋值。
2021-03-25 23:32:54
虽然不太可能,但可能存在依赖于某些元素顺序的 css 规则,例如#example>span+span. 将标记跨度插入内容会破坏那些可能改变/破坏布局的规则。range.getBoundingClientRect() 可能是一个更强大的解决方案。
2021-03-28 23:32:54
当我执行这段代码时,顶部变量是一个对象。可能有一个名为 top 的全局变量。最好写 var left = 0; 无功顶 = 0; 而不是 var left = top = 0;
2021-04-08 23:32:54
@Z-CORE:您说得对:top是对包含当前文档的最外层窗口的引用。我复制并粘贴其他人的代码(在这种情况下来自 quirksmode.org)是非常不寻常的,但至少很明显错误不是我原来的:)
2021-04-13 23:32:54

当我需要内容不受干扰时,我使用 getBoundingClientRect(),同时在它附近放置其他内容。

    var r=window.getSelection().getRangeAt(0).getBoundingClientRect();
    var relative=document.body.parentNode.getBoundingClientRect();
    ele.style.top =(r.bottom -relative.top)+'px';//this will place ele below the selection
    ele.style.right=-(r.right-relative.right)+'px';//this will align the right edges together

这适用于 Chrome,但 IE 喜欢提供奇怪的东西,所以这是一个跨浏览器的解决方案:(在 Chrome 和 IE 中测试,可能适用于其他地方)

https://jsfiddle.net/joktrpkz/7/

嗨,你能解释一下你为什么在你的小提琴中使用 cal1/cal2 吗?谢谢。
2021-03-21 23:32:54
我的要求只是让我的扩展程序在 chrome 下工作,所以这个解决方案对我很有用。
2021-03-27 23:32:54
@Guy 这是为了处理放大时 IE 处理像素的方式。我不记得在其他各种情况下是否有必要。
2021-04-12 23:32:54

您可能应该在“范围”的末尾插入一个绝对位置元素。这在不同浏览器中的工作方式不同,所以最好的办法可能是嗅探。

既然你问了:这就是纽约时报在他们的“altClickToSearch.js”文件中的做法:

function insertButton() {

selectionButton = new Element(
        'span', {
          'className':'nytd_selection_button',
          'id':'nytd_selection_button',
          'title':'Lookup Word',
          'style': 'margin:-20px 0 0 -20px; position:absolute; background:url(http://graphics8.nytimes.com/images/global/word_reference/ref_bubble.png);width:25px;height:29px;cursor:pointer;_background-image: none;filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="http://graphics8.nytimes.com/images/global/word_reference/ref_bubble.png", sizingMethod="image");'
        }
    )

if (Prototype.Browser.IE) {
  var tmp = new Element('div');
  tmp.appendChild(selectionButton);
  newRange = selection.duplicate();
  newRange.setEndPoint( "StartToEnd", selection);
  newRange.pasteHTML(tmp.innerHTML);
  selectionButton = $('nytd_selection_button');
}
else {
  var range = selection.getRangeAt(0);
  newRange = document.createRange();
  newRange.setStart(selection.focusNode, range.endOffset);
  newRange.insertNode(selectionButton);
}
}
谢谢,是的,他们似乎只是在选择中的特定点插入它,而不是绝对插入到文档本身。谢谢!
2021-03-25 23:32:54
那里有一个不必要的浏览器嗅探。您可以只检查您需要的对象/方法。此外,较旧的 WebKits(Safari 2,也许 3,我对此有点模糊,而且我无法轻松掌握所有相关浏览器)中的选择对象没有getRangeAt方法。
2021-03-30 23:32:54
我并不是建议将上面的代码作为最佳解决方案,尤其是在浏览器嗅探的情况下。他在评论中询问了纽约时报。这只是来自该站点的代码。
2021-04-12 23:32:54