Se o texto selecionado e nós selecionados em uma página?
-
21-08-2019 - |
Pergunta
Ao selecionar um bloco de texto (possivelmente abrangendo toda a muitos nós DOM), é possível extrair o texto e os nós selecionado usando Javascript?
Imagine que este código HTML:
<h1>Hello World</h1><p>Hi <b>there!</b></p>
Se o usuário iniciou um evento mouseDown a partir de "World ..." e, em seguida, um mouseUp mesmo logo após "lá!", Eu estou esperando que ele voltaria:
Text : { selectedText: "WorldHi there!" },
Nodes: [
{ node: "h1", offset: 6, length: 5 },
{ node: "p", offset: 0, length: 16 },
{ node: "p > b", offset: 0, length: 6 }
]
Eu tentei colocar o HTML em uma textarea, mas que só vai ficar me o SelectedText. Eu não tentei o elemento <canvas>
mas que pode ser outra opção.
Se não JavaScript, existe uma maneira isso é possível usando uma extensão do Firefox?
Solução
Você está em uma difícil jornada, mas isso é perfeitamente possível. O principal problema é que o IE e W3C expor as interfaces completamente diferentes para as seleções então se você quiser a funcionalidade do navegador cruz, então você basicamente tem que escrever a coisa toda duas vezes. Além disso, algumas funcionalidades básicas está faltando ambas as interfaces.
conexão de desenvolvimento Mozilla tem a história em W3C seleções . Microsoft tem seu sistema documentado no MSDN . Eu recomendo começar em do PPK para faixas .
Aqui estão algumas funções básicas que eu acredito que o trabalho:
// selection objects will differ between browsers
function getSelection () {
return ( msie )
? document.selection
: ( window.getSelection || document.getSelection )();
}
// range objects will differ between browsers
function getRange () {
return ( msie )
? getSelection().createRange()
: getSelection().getRangeAt( 0 )
}
// abstract getting a parent container from a range
function parentContainer ( range ) {
return ( msie )
? range.parentElement()
: range.commonAncestorContainer;
}
Outras dicas
Rangy biblioteca terá sua parte do caminho até lá, unificando as diferentes APIs no IE <9 e todos os outros principais navegadores, e fornecendo uma função getNodes()
na sua gama de objetos:
function getSelectedNodes() {
var selectedNodes = [];
var sel = rangy.getSelection();
for (var i = 0; i < sel.rangeCount; ++i) {
selectedNodes = selectedNodes.concat( sel.getRangeAt(i).getNodes() );
}
return selectedNodes;
}
Obter o texto selecionado é muito fácil em todos os navegadores. Em Rangy é apenas
var selectedText = rangy.getSelection().toString();
Sem Rangy:
function getSelectedText() {
var sel, text = "";
if (window.getSelection) {
text = "" + window.getSelection();
} else if ( (sel = document.selection) && sel.type == "Text") {
text = sel.createRange().text;
}
return text;
}
Quanto às compensações de caracteres, você pode fazer algo assim para qualquer node
nó na seleção. Note que este não representa necessariamente o texto visível no documento porque não leva em conta os espaços em colapso, texto oculto via CSS, texto posicionado fora do fluxo de documentos normais via CSS, quebras de linha implícito <br>
e bloquear elementos, além de outras sutilezas.
var sel = rangy.getSelection();
var selRange = sel.getRangeAt(0);
var rangePrecedingNode = rangy.createRange();
rangePrecedingNode.setStart(selRange.startContainer, selRange.startOffset);
rangePrecedingNode.setEndBefore(node);
var startIndex = rangePrecedingNode.toString().length;
rangePrecedingNode.setEndAfter(node);
var endIndex = rangePrecedingNode.toString().length;
alert(startIndex + ", " + endIndex);
Isso retorna os nós selecionados como eu o entendo: Quando eu tiver
<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p>...
<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p>
um monte de nós e eu selecionar apenas alguns então eu quero apenas esses nós para estar na lista.
function getSelectedNodes() {
// from https://developer.mozilla.org/en-US/docs/Web/API/Selection
var selection = window.getSelection();
if (selection.isCollapsed) {
return [];
};
var node1 = selection.anchorNode;
var node2 = selection.focusNode;
var selectionAncestor = get_common_ancestor(node1, node2);
if (selectionAncestor == null) {
return [];
}
return getNodesBetween(selectionAncestor, node1, node2);
}
function get_common_ancestor(a, b)
{
// from http://stackoverflow.com/questions/3960843/how-to-find-the-nearest-common-ancestors-of-two-or-more-nodes
$parentsa = $(a).parents();
$parentsb = $(b).parents();
var found = null;
$parentsa.each(function() {
var thisa = this;
$parentsb.each(function() {
if (thisa == this)
{
found = this;
return false;
}
});
if (found) return false;
});
return found;
}
function isDescendant(parent, child) {
// from http://stackoverflow.com/questions/2234979/how-to-check-in-javascript-if-one-element-is-a-child-of-another
var node = child;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
function getNodesBetween(rootNode, node1, node2) {
var resultNodes = [];
var isBetweenNodes = false;
for (var i = 0; i < rootNode.childNodes.length; i+= 1) {
if (isDescendant(rootNode.childNodes[i], node1) || isDescendant(rootNode.childNodes[i], node2)) {
if (resultNodes.length == 0) {
isBetweenNodes = true;
} else {
isBetweenNodes = false;
}
resultNodes.push(rootNode.childNodes[i]);
} else if (resultNodes.length == 0) {
} else if (isBetweenNodes) {
resultNodes.push(rootNode.childNodes[i]);
} else {
return resultNodes;
}
};
if (resultNodes.length == 0) {
return [rootNode];
} else if (isDescendant(resultNodes[resultNodes.length - 1], node1) || isDescendant(resultNodes[resultNodes.length - 1], node2)) {
return resultNodes;
} else {
// same child node for both should never happen
return [resultNodes[0]];
}
}
O código deve estar disponível em: https : //github.com/niccokunzmann/spiele-mit-kindern/blob/gh-pages/javascripts/feedback.js
Eu postei esta resposta aqui, porque eu teria gostado de encontrá-lo aqui.
Você pode querer começar a partir http: //javascript.internet .com / page-detalhes / copiar-selecionados-text.html
Há uma maneira muito mais curto se você quiser apenas o intervalo.
function getRange(){
return (navigator.appName=="Microsoft Internet Explorer")
? document.selection.createRange().parentElement()
: (getSelection||document.getSelection)().getRangeAt(0).commonAncestorContainer
}
código compatível com todos os padrões que as obras em IE11 +.
String de texto
window.getSelection().getRangeAt(0).toString()
O começar nó (para trás, mesmo que o texto é selecionado):
window.getSelection().anchorNode
O end nó (para trás, mesmo que o texto é selecionado):
window.getSelection().focusNode
Gostaria de saber mais? Selecione algum texto e execute o seguinte JavaScript no console:
console.log(window.getSelection());
console.log(window.getSelection().getRangeAt(0));