2011-04-09 15 views
14

Estoy trabajando en un sistema de blog simple y estoy usando contenteditable para que los usuarios puedan formatear el texto.Insertar enlace en el elemento contenteditable

Hasta ahora todo funciona como un encanto.

Lo siguiente que quiero es que los usuarios puedan agregar un hipervínculo en el texto.

El usuario tiene que seleccionar (parte de) el texto y hacer clic en el botón de enlace. Después de eso se abre una ventana emergente donde los usuarios deben ingresar la dirección del enlace.

Cuando el usuario hace clic en el botón Aceptar, quiero agregar el enlace al texto que seleccionó en el elemento contenteditable.

¿Cómo puedo implementar esta funcionalidad, ya que no tengo ni idea de cómo hacerlo?

Mi sitio: http://82.170.147.49/blog/3/alpha-release

jsFiddle de mi sitio web: http://jsfiddle.net/qhN9j/

Respuesta

53

document.execCommand() hace esto para usted en todos los principales navegadores:

document.execCommand("CreateLink", false, "http://stackoverflow.com/"); 

para preservar la selección, mientras que su se muestra el diálogo de enlace, se pueden utilizar las siguientes funciones:

function saveSelection() { 
    if (window.getSelection) { 
     sel = window.getSelection(); 
     if (sel.getRangeAt && sel.rangeCount) { 
      var ranges = []; 
      for (var i = 0, len = sel.rangeCount; i < len; ++i) { 
       ranges.push(sel.getRangeAt(i)); 
      } 
      return ranges; 
     } 
    } else if (document.selection && document.selection.createRange) { 
     return document.selection.createRange(); 
    } 
    return null; 
} 

function restoreSelection(savedSel) { 
    if (savedSel) { 
     if (window.getSelection) { 
      sel = window.getSelection(); 
      sel.removeAllRanges(); 
      for (var i = 0, len = savedSel.length; i < len; ++i) { 
       sel.addRange(savedSel[i]); 
      } 
     } else if (document.selection && savedSel.select) { 
      savedSel.select(); 
     } 
    } 
} 

ejemplo jsFiddle: http://jsfiddle.net/JRKwH/1/

ACTUALIZACIÓN

Para controlar la enlace (s) creado (si es que se crearon en absoluto) es complicado. Puede usar mi propia biblioteca Rangy:

var sel = rangy.getSelection(); 
if (sel.rangeCount) { 
    var links = sel.getRangeAt(0).getNodes([1], function(el) { 
     return el.nodeName.toLowerCase() == "a"; 
    }); 
    alert(links.length); 
} 

...o algo como lo siguiente:

function getLinksInSelection() { 
    var selectedLinks = []; 
    var range, containerEl, links, linkRange; 
    if (window.getSelection) { 
     sel = window.getSelection(); 
     if (sel.getRangeAt && sel.rangeCount) { 
      linkRange = document.createRange(); 
      for (var r = 0; r < sel.rangeCount; ++r) { 
       range = sel.getRangeAt(r); 
       containerEl = range.commonAncestorContainer; 
       if (containerEl.nodeType != 1) { 
        containerEl = containerEl.parentNode; 
       } 
       if (containerEl.nodeName.toLowerCase() == "a") { 
        selectedLinks.push(containerEl); 
       } else { 
        links = containerEl.getElementsByTagName("a"); 
        for (var i = 0; i < links.length; ++i) { 
         linkRange.selectNodeContents(links[i]); 
         if (linkRange.compareBoundaryPoints(range.END_TO_START, range) < 1 && linkRange.compareBoundaryPoints(range.START_TO_END, range) > -1) { 
          selectedLinks.push(links[i]); 
         } 
        } 
       } 
      } 
      linkRange.detach(); 
     } 
    } else if (document.selection && document.selection.type != "Control") { 
     range = document.selection.createRange(); 
     containerEl = range.parentElement(); 
     if (containerEl.nodeName.toLowerCase() == "a") { 
      selectedLinks.push(containerEl); 
     } else { 
      links = containerEl.getElementsByTagName("a"); 
      linkRange = document.body.createTextRange(); 
      for (var i = 0; i < links.length; ++i) { 
       linkRange.moveToElementText(links[i]); 
       if (linkRange.compareEndPoints("StartToEnd", range) > -1 && linkRange.compareEndPoints("EndToStart", range) < 1) { 
        selectedLinks.push(links[i]); 
       } 
      } 
     } 
    } 
    return selectedLinks; 
} 

jsFiddle: http://jsfiddle.net/JRKwH/3/

+3

Sr. Down. ¡Eres mi héroe de todos los tiempos! Finalmente puedo deshacerme de esa fea ventana de avisos de javascript;) Aunque creo que clmarquart tiene un punto válido. Lo siguiente que quiero hacer es crear un enlace con un objetivo (_blank) y creo que voy a necesitar insertHTML (u otro) para esto. ¿Es posible hacer que su código funcione agregando un objetivo a la etiqueta 'a'? ¡Si puedes, no solo eres mi héroe, sino que mejoraré tu estado a Dios! :) – PeeHaa

+2

@PeeHaa: Buena pregunta. Va a ser complicado porque el comando maneja múltiples casos. En ocasiones, no se creará ningún enlace (si el contenido seleccionado ya está dentro de un enlace con la misma URL) o solo se vinculará contenido que no esté dentro de otro enlace. Añadiré un poco a mi respuesta. –

+2

@PeeHaa: actualizado. –

7

como Alfred dijo que hay editores ya bien desarrollados, sobre todo para las funciones básicas. Puede restringirlo para usar las pocas o tantas funciones que desee.

La parte más difícil en el desarrollo desde cero, es que todos los navegadores actúan de forma ligeramente diferente. La siguiente debe conseguirá que se mueve en la dirección correcta en la mayoría de los navegadores, que no sea IE:

var selected = document.getSelection(); 
document.execCommand("insertHTML",false,"<a href='"+href+"'>"+selected+"</a>"); 
+0

Gracias clmarquart. Funciona para la mayoría de los navegadores. Realmente no esperaba que funcionaría bajo IE por defecto. What else is new :) – PeeHaa

+0

Lo único que sucede ahora es que cuando intento ingresar una URL, la selección ya no está configurada en mi texto. Entonces no puedo agregar el hipervínculo. ¿Hay alguna forma de preservar/restablecer la selección antes del comando exec? – PeeHaa

+1

Puesto que usted está usando un ''

para su área contentEditable, que está utilizando el mismo elemento de documento, por lo que lo que sucede es cuando se hace clic en cualquier lugar que va a perder la selección anterior. Intente usar la tecla 'Enter' como forma de enviar, en lugar de hacer clic en el icono de verificación. De lo contrario, puede obtener más información al conservar el rango, consulte [Documentos de Mozilla] (https://developer.mozilla.org/es/DOM/Selection) – clmarquart

2

EDITAR No es posible en el IE en ExecCommand, porque no podemos insertar comillas en 'href', hay que hacerlo en javascript puro con rango:

// EN DIV EN UNA IFRAME

// Get the frame 
var iframe = document.getElementById('myframe'); 

// Selection object in the frame 
theSelection = iframe.contentWindow.getSelection(); 

// position of the selection to insert 
theRange = theSelection.getRangeAt(0); 

// get content inside the original selection (and delete content in) 
var fragment = theRange.extractContents(); 

// Create a new link in frame 
var newLink = iframe.contentWindow.document.createElement('a'); 

// Create a text element with the fragment to put in the link 
var theText = document.createTextNode(fragment.textContent); 

// URL 
theLink.href = '#'; 

// Title 
theLink.title = 'title'; 

// Attribute 'onclick' 
theLink.setAttribute('onclick', thelink); 

// Target 
theLink.target = '_blank'; 

// Add the text in the link 
theLink.appendChild(theText); 

// Insert the link at the range 
theRange.insertNode(newLink); 

// DIV sin marcos

// Selection object in the window 
theSelection = window.getSelection(); 

// begin of the selection to insert 
theRange = theSelection.getRangeAt(0); 

// get content inside the original selection (and delete content in) 
var fragment = theRange.extractContents(); 

// Create a new link in the document 
var newLink = document.createElement('a'); 

// Create a text element with the fragment to put in the link 
var theText = document.createTextNode(fragment.textContent); 

// URL 
theLink.href = '#'; 

// Title 
theLink.title = 'title'; 

// Attribute 'onclick' 
theLink.setAttribute('onclick', thelink); 

// Target 
theLink.target = '_blank'; 

// Add the text in the link 
theLink.appendChild(theText); 

// Insert the link at the range 
theRange.insertNode(newLink); 
0

lo haría de esta manera:

  1. Crear un vínculo con una (posiblemente única) inicial falsa href atributo para identificar de largo.
  2. Fetch ese elemento utilizando document.querySelector('a[href=<unique-href>]').
  3. Ahora tiene una referencia al elemento creado y puede hacer con ella lo que quieras.

El beneficio de esto es que no tiene que trabajar con Selection en absoluto.

5

respuesta mejor mirando:

function link() { 
 
    if (window.getSelection().toString()) { 
 
    var a = document.createElement('a'); 
 
    a.href = 'http://www.google.com'; 
 
    a.title = 'GOOGLE'; 
 
    window.getSelection().getRangeAt(0).surroundContents(a); 
 
    } 
 
}
select some of text then click link button! 
 
<button onclick='link()'>link text to google</button>

Este método puede ser aplicado en cualquier lugar y no requiere que el elemento sea contenteidtable.

puede agregar cualquier evento o atributo al nuevo elemento A como otros elementos.

El window.getSelection().toString() comprueba si realmente se ha seleccionado algún texto. Funciona bien en Chrome, no tengo IE para probar, de todos modos hay otros métodos para verificarlo. Pero surroundContents() que es la parte clave está disponible en IE9 según lo sugerido por MDN.

Finalmente, sugiero usar un iFrame en lugar de contenteditable div, por lo que no habrá problemas para preservar la selección.

+0

Esto es simple y elegante. Debe ser la respuesta de aceptación. Donde puede encontrar una lista completa de todas las interacciones '.execCommand()'. Parecen geniales pero dudo en cambiarme de jQuery. –

Cuestiones relacionadas