2009-10-19 5 views
20

Je dois positionner un bouton positionné en absolu à côté du texte sélectionné par l'utilisateur. juste comme IE8 fait intérieurement.Comment puis-je positionner un élément à côté de la sélection du texte utilisateur?

Je lie un événement jQuery mouseup au document, et obtenir le texte sélectionné, mais je suis actuellement à court d'idées sur la façon de savoir où la sélection réelle est positionnée, sans l'envelopper dans un élément, parce que la sélection du texte peut être à travers plusieurs éléments, et cela gâcherait la structure si je l'enveloppais.

+0

http: //www.nytimes.com fait cela dans leurs articles – vsync

Répondre

23

Vous pouvez placer une durée de marqueur à la fin de la sélection, obtenir ses coordonnées En utilisant jQuery, placez votre bouton à ces coordonnées et supprimez la durée du marqueur.

Ce qui suit devrait vous aider à démarrer:

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

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

    var selectionEl; 

    return function() { 
     var sel, range; 

     if (document.selection && document.selection.createRange) { 
      // Clone the TextRange and collapse 
      range = document.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 = document.getElementById(markerId); 
     } else if (window.getSelection) { 
      sel = window.getSelection(); 

      if (sel.getRangeAt) { 
       range = sel.getRangeAt(0).cloneRange(); 
      } else { 
       // Older WebKit doesn't have getRangeAt 
       range = document.createRange(); 
       range.setStart(sel.anchorNode, sel.anchorOffset); 
       range.setEnd(sel.focusNode, sel.focusOffset); 

       // Handle the case when the selection was selected backwards (from the end to the start in the 
       // document) 
       if (range.collapsed !== sel.isCollapsed) { 
        range.setStart(sel.focusNode, sel.focusOffset); 
        range.setEnd(sel.anchorNode, sel.anchorOffset); 
       } 
      } 

      range.collapse(false); 

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

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

       document.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); 
     } 
    }; 
})(); 
+0

Lorsque j'ai exécuté ce code, la variable supérieure était un objet. Probablement il y avait une variable globale appelée top. Il est préférable d'écrire var left = 0; var top = 0; au lieu de var left = top = 0; –

+0

@ Z-CORE: Vous avez absolument raison: 'top' est une référence à la fenêtre la plus externe contenant le document en cours. Il est très inhabituel pour moi d'avoir copié et collé le code de quelqu'un d'autre (à partir de quirksmode.org dans ce cas), mais au moins il est clair que l'erreur n'est pas à l'origine mienne :) –

+0

Bien que peu probable, il peut exister des règles CSS qui dépendent de certains commandes d'éléments comme '#example> span + span'. l'insertion de l'étendue du marqueur dans le contenu perturberait ces règles en modifiant ou en cassant la mise en page. Le range.getBoundingClientRect() pourrait être une solution plus robuste. – Rick

2

Vous devez probablement insérer un élément de position absolue à la fin de la 'plage'. Cela fonctionne différemment dans différents navigateurs, donc votre meilleur pari pourrait être de renifler.

Et puisque vous avez demandé: voilà comment les New York Times, il fait dans son dossier « 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

Merci, oui, ils semblent juste l'insérer à ce point précis dans la sélection au lieu d'absolu au document lui-même. Merci! – vsync

+3

Il y a un renifleur de navigateur inutile. Vous pouvez simplement vérifier les objets/méthodes dont vous avez besoin. De plus, l'objet de sélection dans les anciens WebKits (Safari 2, peut-être 3, je suis un peu flou sur ce point et je n'ai pas tous les navigateurs pertinents à portée de main) n'a pas de méthode 'getRangeAt'. –

+0

Je ne suggérais pas le code ci-dessus comme la meilleure solution, surtout avec le navigateur sniff. Il a demandé au sujet des nytimes dans son commentaire. C'est juste le code de ce site. –

8

J'utilise getBoundingClientRect() quand j'ai besoin le contenu de rester intacte, tout en plaçant du contenu supplémentaire à proximité.

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 

cela fonctionne dans Chrome, mais IE aime donner des choses étranges, voici donc une solution multi-navigateur: (testé dans Chrome et IE, fonctionne probablement ailleurs)

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

+0

Salut, pouvez-vous expliquer pourquoi utilisez-vous cal1/cal2 dans votre violon? Merci. –

+0

@Guy C'était pour faire face à la façon dont IE gère les pixels en zoomant. Je ne me souviens pas si c'était nécessaire ou non dans divers autres scénarios. – Rick

+0

mon exigence est seulement pour mon extension de travailler sous chrome donc cette solution fonctionne très bien pour moi. – SKYnine

Questions connexes