Alterar CSS do texto selecionado usando Javascript

Eu estou tentando fazer um bookmarklet javascript que funcionará como um marcador, alterando o plano de fundo do texto selecionado em uma página da Web para amarelo quando o bookmarklet é pressionado.

Estou usando o código a seguir para obter o texto selecionado e ele funciona bem, retornando a string correta

function getSelText() { var SelText = ''; if (window.getSelection) { SelText = window.getSelection(); } else if (document.getSelection) { SelText = document.getSelection(); } else if (document.selection) { SelText = document.selection.createRange().text; } return SelText; 

}

No entanto, quando criei uma function semelhante para alterar o CSS do texto selecionado usando jQuery, não está funcionando:

 function highlightSelText() { var SelText; if (window.getSelection) { SelText = window.getSelection(); } else if (document.getSelection) { SelText = document.getSelection(); } else if (document.selection) { SelText = document.selection.createRange().text; } $(SelText).css({'background-color' : 'yellow', 'font-weight' : 'bolder'}); 

}

Alguma ideia?

A maneira mais fácil de fazer isso é usar execCommand() , que possui um comando para alterar a cor do plano de fundo em todos os navegadores modernos.

O seguinte deve fazer o que você quiser em qualquer seleção, incluindo os que abrangem vários elementos. Em navegadores não-IE, ele ativa o designMode , aplica uma cor de segundo plano e, em seguida, desativa novamente o designMode .

ATUALIZAR

Corrigido no IE 9.

 function makeEditableAndHighlight(colour) { var range, sel = window.getSelection(); if (sel.rangeCount && sel.getRangeAt) { range = sel.getRangeAt(0); } document.designMode = "on"; if (range) { sel.removeAllRanges(); sel.addRange(range); } // Use HiliteColor since some browsers apply BackColor to the whole block if (!document.execCommand("HiliteColor", false, colour)) { document.execCommand("BackColor", false, colour); } document.designMode = "off"; } function highlight(colour) { var range, sel; if (window.getSelection) { // IE9 and non-IE try { if (!document.execCommand("BackColor", false, colour)) { makeEditableAndHighlight(colour); } } catch (ex) { makeEditableAndHighlight(colour) } } else if (document.selection && document.selection.createRange) { // IE < = 8 case range = document.selection.createRange(); range.execCommand("BackColor", false, colour); } } 

Aqui está um exemplo grosseiro de como isso poderia funcionar. Como Zack aponta, você precisa estar ciente dos casos em que a seleção abrange vários elementos. Isso não deve ser usado como está, apenas algo para ajudar a gerar ideias. Testado no Chrome.

 var selection = window.getSelection(); var text = selection.toString(); var parent = $(selection.focusNode.parentElement); var oldHtml = parent.html(); var newHtml = oldHtml.replace(text, ""+text+""); parent.html( newHtml ); 

No Firefox, você pode usar a class psuedo ::-moz-selection .
No Webkit, você pode usar a pseudo-class ::selection .

Veja um pequeno exemplo que fiz em http://www.jsfiddle.net/hbwEE/3/

Ele não leva em conta as seleções que abrangem vários elementos .. ( IE vai fazer, mas vai mexer um pouco no html .. )

Para tornar o stick de destaque permanentemente, acredito que você terá que envolver a seleção em um novo elemento DOM ( span deve fazer), ao qual você pode append propriedades de estilo. Eu não sei se o jQuery pode fazer isso por você. Lembre-se de que as seleções podem ultrapassar os limites dos elementos, portanto, no caso geral, você terá que injetar um monte de novos elementos

Eu gosto da resposta de Tim, é limpa e rápida. Mas também desliga as portas para interações com os destaques.

Inserir elementos inline diretamente ao redor dos textos é uma má escolha, pois eles quebram o stream de texto e bagunçam as coisas em situações complexas,

Então eu sugiro um hack sujo que

  1. calcula o layout absoluto de cada linha do texto selecionado (não importa onde eles estejam),
  2. em seguida, insira elementos de blocos inline coloridos e semitransparentes no final do corpo do documento.

Essa extensão do Chrome é um exemplo de como isso pode ser feito.

Ele usa a API dessa biblioteca para obter o layout absoluto de cada linha selecionada.