32 Stimmen

Position des ausgewählten Textes ermitteln und ein Element daneben platzieren

Tl;dr

Die Idee ist, dass ein Benutzer einen beliebigen Text markieren kann und ein Menü direkt neben der Auswahl erscheint, das mögliche Aktionen für den ausgewählten Text enthält.


Ich muss eine absolut positionierte Schaltfläche neben dem ausgewählten Text des Benutzers positionieren.

Ich binde eine Mouseup-Ereignis auf das Dokument und den ausgewählten Text zu erhalten, aber ich bin derzeit aus Ideen, wie zu wissen, wo die tatsächliche Auswahl positioniert ist, ohne es in irgendeinem Element zu wickeln, weil die Auswahl von Text über mehrere Elemente sein kann, und es würde die Struktur durcheinander, wenn ich es wickeln würde.

30voto

Tim Down Punkte 304837

Sie könnten eine Markierungsspanne am Ende der Auswahl positionieren, ihre Koordinaten mit jQuery ermitteln, Ihre Schaltfläche an diesen Koordinaten platzieren und die Markierungsspanne entfernen.

Die folgenden Hinweise sollen Ihnen den Einstieg erleichtern:

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);
        }
    };
})();

1 Stimmen

Als ich diesen Code ausführte, war die Variable top ein Objekt. Wahrscheinlich gab es eine globale Variable namens top. Es ist besser, var left = 0; var top = 0; anstelle von var left = top = 0 zu schreiben;

0 Stimmen

@Z-CORE: Sie haben absolut Recht: top ist ein Verweis auf das äußerste Fenster, das das aktuelle Dokument enthält. Es ist sehr ungewöhnlich, dass ich den Code von jemand anderem kopiert und eingefügt habe (in diesem Fall von quirksmode.org), aber zumindest ist klar, dass der Fehler nicht ursprünglich von mir stammt :)

2 Stimmen

Auch wenn es unwahrscheinlich ist, kann es CSS-Regeln geben, die von bestimmten Elementreihenfolgen abhängen, wie #example>span+span . Das Einfügen der Markierungsspanne in den Inhalt würde diese Regeln unterbrechen und möglicherweise das Layout verändern/zerstören. Die Funktion range.getBoundingClientRect() könnte eine robustere Lösung sein.

23voto

Rick Punkte 1197

Ich verwende getBoundingClientRect(), wenn ich möchte, dass der Inhalt ungestört bleibt, während ich zusätzliche Inhalte in seiner Nähe platziere.

    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

das funktioniert in Chrome, aber der IE gibt gerne seltsame Dinge von sich, daher hier eine browserübergreifende Lösung: (Getestet in Chrome und IE, funktioniert wahrscheinlich auch anderswo)

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

1 Stimmen

Hallo, können Sie erklären, warum Sie cal1/cal2 in Ihrem Fiddle verwenden? danke.

0 Stimmen

@Guy Das lag an der Art und Weise, wie der IE mit Pixeln umgeht, wenn man sie vergrößert. Ich weiß nicht mehr, ob es in verschiedenen anderen Szenarien notwendig war oder nicht.

0 Stimmen

Meine Anforderung sind nur für meine Erweiterung unter Chrome zu arbeiten, so dass diese Lösung ist für mich arbeiten groß.

2voto

Alex Sexton Punkte 10333

Sie sollten wahrscheinlich ein Element mit absoluter Position am Ende des "Bereichs" einfügen. Dies funktioniert in verschiedenen Browsern unterschiedlich, so dass Ihre beste Wette könnte sein, zu schnüffeln.

Und da Sie gefragt haben: So macht es die New York Times in ihrer Datei "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);
}
}

0 Stimmen

Danke, ja, sie scheinen es nur an diesem bestimmten Punkt in die Auswahl einzufügen, anstatt absolut in das Dokument selbst. danke!

3 Stimmen

Da ist ein unnötiger Browser-Sniff drin. Sie können einfach nach den Objekten/Methoden suchen, die Sie benötigen. Außerdem hat das Auswahlobjekt in älteren WebKits (Safari 2, vielleicht 3, ich bin mir nicht ganz sicher, und ich habe nicht alle relevanten Browser zur Hand) keine getRangeAt Methode.

0 Stimmen

Ich habe den obigen Code nicht als die beste Lösung vorgeschlagen, insbesondere mit dem Browser-Sniff. Er fragte in seinem Kommentar nach der "Nytimes". Dies ist nur der Code von dieser Seite.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X