Tenho feito meus estudos em javaScript e hoje gostaria de apresentar o resultado de um código estruturado (não OO) em javaScript: Um HTMLEDitor.
Eu ví um exemplo de como se navegar entre os DOMs e reimplementei os metodos getTextNodesIn e setSelectionRange. Fonte original:
http://stackoverflow.com/questions/6240139/highlight-text-range-using-javascript
Dicas:
1) Pra pegar a seleção do range, é necessário 4 coisas:
o nó onde começa a seleção: startContainer
a posição dentro do nó ende começa a seleção: startOffset
o nó onde termina a seleção: endContainer
e a posição dentro do no onde termina a seleção.: endOffset
2) Prorpiedades da Div
contentEditable = true
Isso determina que a DIV pode ser edivavel.
style.whiteSpace = "pre";
Isso determina que a DIV trabalhe com espaços e enteres no formato "\n" ao invez de "<BR>"
3) o metodo mais complicado é setProperty.
Notem que ele é um método recursivo e seu pseudo código é esse:
setProperty = function (div, node, atributo, valor)
{
verificar se é a primeira chamada do metodo
se for, cria um marcador para sabermos
se foi encontrado os nos iniciais e finais
e pega o range que armazena os dados da
seleção
se não existir seleção, apesar do cursor estar na div
retorna a mesma coisa. OU seja, não vai ser feito
nenhuma alteração.
Verifica se o nó é um textNode.
{
se for, é necessario saber que:
os nós possiveis serão de 5 tipos:
no sem nenhuma seleção (parte inicial e sem seleção de todo o texto)
no com seleção parcial, onde o final é o selecionado
no com seleção total (todo o nó)
no com seleção parcial, onde o inicio é o selecionado
no sem nenhuma seleção (parte final e sem seleção de todo o texto)
A primeira condição é a mais fácil
se o no é igual ao inicial e ao final do range
{
marca que encontrou o no inicial e final
existe a possibilidade desse texto ser dividida em 3 partes:
texto inicial, que não tem seleção
texto da seleção
texto final, sem seleção nenhuma.
}
se não, se o nó é igual ao inicial do range
{
marca que encontrou o no inicial
existe a possibilidade desse texto ser dividida em 2 partes:
texto inicial, que não tem seleção
texto da seleção
}
se não, se o nó é igual ao final do range
{
marca que encontrou o no final
existe a possibilidade desse texto ser dividida em 2 partes:
texto da seleção
texto final, que não tem seleção
}
aqui, é o tratamento para os nos que não fazem parte do range.
nossos marcadores serão analizados aqui.
os que vem antes e depois da seleção não precisam ser alterados
já o que está no meio da seleção, irá receber as novas propriedades
antes da seleção : não achou inicio da seleção
no meio da seleção : achou inicio mas não achou o fim da seleção.
recebetratamento.
ou depois da seleção: achou inicio e fim da seleção
}
se não for
{
realiza a recursividade nos nós filhos
}
}
Esboço:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript">
Selector = function()
{
if (window.getSelection)
{
return window.getSelection();
}
else if (document.getSelection)
{
return document.getSelection();
}
}
createRange = function (cell)
{
var rng=null;
if(cell.createRange)
{
rng=cell.createRange();
}
else if(cell.getRangeAt)
{
rng=cell.getRangeAt(0);
if(rng.toString()=="") rng=null;
}
return rng;
}
getTextNodesIn = function (node)
{
var textNodes = [];
if (node!=undefined && node.nodeType !=undefined && node.nodeType == Node.TEXT_NODE )
{
textNodes.push(node);
}
else
{
var children = node.childNodes;
for (var i = 0, len = children.length; i < len; ++i)
{
textNodes = textNodes.concat( getTextNodesIn(children[i]) );
}
}
return textNodes;
}
getCursorPosition = function (div)
{
var se = Selector ();
var range = se.getRangeAt(0);
textNodes = getTextNodesIn(div);
countChar=0;
for (var i = 0; i<textNodes.length; i++ )
{
if ( range.startContainer==textNodes[i] )
{
return countChar+range.startOffset
}
countChar += textNodes[i].textContent.length
}
return null;
}
getStartContainer = function (div)
{
var se = Selector ();
var range = se.getRangeAt(0);
textNodes = getTextNodesIn(div);
return range.startContainer;
}
getSelectionPosition = function (div)
{
var se = Selector ();
var range = se.getRangeAt(0);
textNodes = getTextNodesIn(div);
endCharCount = 0;
Int=null
end=null
for (var i = 0; i<textNodes.length; i++ )
{
if ( range.startContainer==textNodes[i] )
{
Int = endCharCount+range.startOffset
}
if ( range.endContainer==textNodes[i] )
{
end = endCharCount+range.endOffset
break;
}
endCharCount += textNodes[i].textContent.length
}
return [Int, end];
}
setSelectionPosition = function (div, start, end)
{
range = createRange(document);
range.selectNodeContents(div);
var foundStart = false;
var charCount=0;
textNodes = getTextNodesIn(div);
var aa=0
var bb=0
for (var i = 0; i<textNodes.length; i++ )
{
velhoCount = charCount;
charCount += textNodes[i].textContent.length
if (!foundStart && velhoCount <= start && start <= charCount )
{
aa = textNodes[i].textContent.length - (charCount - start );
range.setStart(textNodes[i], aa );
foundStart = true;
}
if (foundStart && end <= charCount)
{
bb = textNodes[i].textContent.length - (charCount - end) ;
range.setEnd(textNodes[i], bb );
break;
}
}
var sel = Selector();
sel.removeAllRanges();
sel.addRange(range);
div.focus();
}
setCursorPosition = function (div, position)
{
range = createRange(document);
range.selectNodeContents(div);
var charCount = 0
var endCharCount;
textNodes = getTextNodesIn(div);
for (var i = 0; i<textNodes.length; i++ )
{
textNode = textNodes[i];
endCharCount = charCount + textNode.length;
if (position >= charCount && position <= endCharCount )
{
range.setStart(textNode, position - charCount);
range.setEnd(textNode, position - charCount);
range.collapse(false);
break;
}
charCount = endCharCount;
}
var sel = Selector();
sel.removeAllRanges();
sel.addRange(range);
div.focus();
}
enterEvent = function (event)
{
if (event.keyCode == 13)
{
event.preventDefault();
var position = getCursorPosition (this)
var se = Selector ();
var range = se.getRangeAt(0);
var posA = 0 ;
var posB = range.endOffset ;
var posC = range.endContainer.textContent.length ;
var a = range.endContainer.textContent.substring( posA, posB ) ;
var b = range.endContainer.textContent.substring( posB, posC ) ;
range.endContainer.parentNode.innerHTML = (a + '\n' + b) ;
setCursorPosition (this, position+1)
}
}
setStyle = function (tag, atributo, valor)
{
tag.style[atributo] = valor;
}
setProperty = function (div, node, atributo, valor)
{
if(node==undefined || node==null )
{
div.achouEndContainer = false;
div.achouStartContainer = false;
var se = Selector ();
div.range = se.getRangeAt(0);
node = div
}
if( div.range.startContainer == div.range.endContainer && div.range.startOffset==div.range.endOffset )
{
return;
}
if( node.nodeType == Node.TEXT_NODE )
{
var conteudo = node.textContent ;
var posA = 0 ;
var posB = div.range.startOffset ;
var posC = div.range.endOffset ;
var posD = conteudo.length ;
if(node == div.range.startContainer && node == div.range.endContainer )
{
div.achouEndContainer = true;
div.achouStartContainer = true;
var span = document.createElement("span");
var s1 = document.createElement("span");
s1.textContent = conteudo.substring( posA, posB )
span.appendChild(s1)
var s2 = document.createElement("span");
setStyle(s2, atributo, valor)
s2.textContent = conteudo.substring( posB, posC )
span.appendChild(s2)
var s3 = document.createElement("span");
s3.textContent = conteudo.substring( posC, posD)
span.appendChild(s3)
var rec = node.parentNode;
rec.removeChild(node);
rec.appendChild( span ) ;
}
else if( node == div.range.startContainer)
{
div.achouStartContainer = true;
var span = document.createElement("span");
var s1 = document.createElement("span");
s1.textContent = conteudo.substring( posA, posB )
span.appendChild(s1)
var s2 = document.createElement("span");
setStyle(s2, atributo, valor)
s2.textContent = conteudo.substring( posB, posD )
span.appendChild(s2)
var rec = node.parentNode;
rec.removeChild(node);
rec.appendChild( span ) ;
}
else if( node == div.range.endContainer)
{
div.achouEndContainer = true;
var span = document.createElement("span");
var s1 = document.createElement("span");
setStyle(s1, atributo, valor);
s1.textContent = conteudo.substring( posA, posC )
span.appendChild(s1)
var s2 = document.createElement("span");
s2.textContent = conteudo.substring( posC, posD )
span.appendChild(s2)
var rec = node.parentNode;
rec.removeChild(node);
rec.appendChild( span ) ;
}
else if( !div.achouStartContainer )
{
}
else if( div.achouStartContainer && !div.achouEndContainer )
{
var span = document.createElement("span");
setStyle(span, atributo, valor);
var s1 = document.createTextNode(conteudo);
span.appendChild(s1);
var rec = node.parentNode;
rec.removeChild(node);
rec.appendChild( span ) ;
}
else if( div.achouEndContainer )
{
}
}
else
{
var children = node.childNodes;
for (var i = 0; i<children.length; i++)
{
setProperty( div, children[i], atributo, valor);
}
}
}
load = function()
{
var divA = document.getElementById("div");
divA.style.whiteSpace = "pre";
divA.contentEditable = true;
divA.style.position = "absolute";
divA.style.border = "1px solid #000000";
divA.style.position = "absolute";
divA.style.left = "30px";
divA.style.top = "30px";
divA.style.width = "450";
divA.style.height = "150";
divA.style.border = "1px solid #000000";
divA.innerHTML = "<span>abcdefghij 1234567890a\n1234567890</span>" ;
divA.onkeydown = enterEvent;
}
teste1 = function()
{
var rec = document.getElementById("div")
obj = getSelectionPosition(rec)
setProperty (rec, null, 'color', 'red');
setSelectionPosition(rec, obj[0], obj[1] )
}
teste2 = function()
{
var rec = document.getElementById("div")
obj = getSelectionPosition(rec)
setProperty (rec, null, 'color', 'blue');
setSelectionPosition(rec, obj[0], obj[1] )
}
teste3 = function()
{
var rec = document.getElementById("div")
obj = getSelectionPosition(rec)
setProperty (rec, null, 'fontWeight', 'bold');
setSelectionPosition(rec, obj[0], obj[1] )
}
teste4 = function()
{
var rec = document.getElementById("div")
obj = getSelectionPosition(rec)
setProperty (rec, null, 'fontWeight', 'normal');
setSelectionPosition(rec, obj[0], obj[1] )
}
teste5 = function()
{
var rec = document.getElementById("div")
obj = getSelectionPosition(rec)
setProperty (rec, null, 'fontStyle', 'italic');
setSelectionPosition(rec, obj[0], obj[1] )
}
teste6 = function()
{
var rec = document.getElementById("div")
obj = getSelectionPosition(rec)
setProperty (rec, null, 'fontStyle', 'normal');
setSelectionPosition(rec, obj[0], obj[1] )
}
</script>
</head>
<body onload="load()">
<div id="div">sdf</div>
<button type="button" onclick="teste1()">vermelho</button>
<button type="button" onclick="teste2()">azul</button>
<button type="button" onclick="teste3()">bold</button>
<button type="button" onclick="teste4()">unbold</button>
<button type="button" onclick="teste5()">italic</button>
<button type="button" onclick="teste6()">normal text</button>
</body>
</html>