Nodes opnieuw indelen
Ik heb een aantal <pre> elementen. In die elementen staan <span> tags, en gewone text nodes.
Het is ongeveer zo:
Code (php)
1
2
3
2
3
<pre>Tekst</pre>
<pre><span>In span</span> buiten span <span>in span</span></pre>
<pre>...</pre>
<pre><span>In span</span> buiten span <span>in span</span></pre>
<pre>...</pre>
Nu wil ik met javascript een span kunnen invoegen op een plek in de tekst. Je zou misschien zegggen dat je dit met innerHTML heel makkelijk kunt doen. Dat is dus niet zo, want innerHTML geeft de letterlijke html code.
Als voorbeeld neem ik de tweede <pre>
De textContent van de tweede <pre> is: In span buiten span in span
De innerHTML van de tweede <pre> is: <span>In span</span> buiten span <span>in span</span>
Ik wil dus een span invoegen op bijv. de tiende plek in de tekst.
Je kunt het zien als een soort cursor die je ergens in de tekst neerzet. De bestaande span elementen mogen niet worden beïnvloed hierdoor. Hoe doe ik dit? Ik heb echt al van alles geprobeerd.
Gewijzigd op 19/07/2014 23:42:12 door Mark Hogeveen
Kijk eens naar document.createElement()
Met document.createElement() kun je elementen aanmaken en die een bepaalde plek in de DOM tree gegeven, bijv. als child van (element.appendChild()) of voor een element (element.insertBefore()). In jouw geval wil je een element in een tekst plaatsen, tekst wordt in een DOM tree als een enkele TextNode gezien, daar kun je dus niks in plaatsen.
Je zult dus de Text Node moeten opvragen en die dan splitten in een array. Vervolgens op een bepaalde plek in die array een open tag moeten zetten en het element erna een sluit tag en dan de array omzetten tot een string en weer invoegen als innerHTML van het element.
Bijv. http://jsbin.com/bubew/2/edit
Code (js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
HTMLElement.prototype.addElementAtPosition = function (position, openTag, split) {
split || (split = ' ');
if (this.childElementCount) {
throw('addElementAtPosition can only be used when the element contains only text');
}
var textPositions = this.innerHTML.split(split);
textPositions.splice(position, 0, openTag);
textPositions.splice(position + 2, 0, '</' + openTag.substr(1));
this.innerHTML = textPositions.join(split);
};
split || (split = ' ');
if (this.childElementCount) {
throw('addElementAtPosition can only be used when the element contains only text');
}
var textPositions = this.innerHTML.split(split);
textPositions.splice(position, 0, openTag);
textPositions.splice(position + 2, 0, '</' + openTag.substr(1));
this.innerHTML = textPositions.join(split);
};
Gewijzigd op 20/07/2014 13:34:35 door Wouter J
Je moet als eerste niet de innerHTML uitlezen maar een array van childNodes die in je <pre> zitten.
Vervolgens kun je insertBefore() gebruiken om een nieuwe Node voor een bestaande node in te voegen.
http://codepen.io/anon/pen/Cpgrq
(btw, dit is een mooie app om snel de DOM nodes te zien, dan hoef je niet zelf het script te schrijven :) http://software.hixie.ch/utilities/js/live-dom-viewer/)
Toevoeging op 20/07/2014 14:47:51:
#text =>
SPAN => 'hallo'
#text => 'dit is'
SPAN => 'leuk'
#text =>
zo zou je array er uit kunnen zien.
Het gaat niet om het onderscheid tussen stukjes tekst gescheiden door spaties.
Als ik deze tekst heb:
Ze hebben weer regen voorspeld voor morgen
En ik wil op de zesde 'plek' een span invoegen, dan moet je dat zien als:
Ze heb|ben weer regen voorspeld voor morgen
En dus niet:
Ze hebben weer regen voorspeld | voor morgen
Bij dit invoegen van de span, mogen niet de bestaande elementen die eventueel in de tekst staan beschadigd worden.
Ik deed eerst dit:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function insertAt(obj, pos, elemToInsert) {
var nodesBefore = [];
var nodesAfter = [];
var lengthCounter = 0;
var nodes = obj.childNodes;
for(var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if(lengthCounter + node.textContent.length <= pos) {
nodesBefore.push(node);
lengthCounter += node.textContent.length;
}
if(lengthCounter + node.textContent.length > pos) {
var ntBefore = node.textContent.substring(0, pos - lengthCounter);
var nBefore = document.createTextNode(ntBefore);
nodesBefore.push(nBefore);
var ntAfter = node.textContent.substring(pos - lengthCounter, node.textContent.length);
console.log(ntAfter);
var nAfter = document.createTextNode(nAfter);
nodesAfter.push(nAfter);
}
}
var nodeSet = [];
nodeSet.concat(nodesBefore);
nodeSet.push(elemToInsert);
nodeSet.concat(nodesAfter);
return nodeSet; // Is nu een array van de hele inhoud van het pre element
}
var nodesBefore = [];
var nodesAfter = [];
var lengthCounter = 0;
var nodes = obj.childNodes;
for(var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if(lengthCounter + node.textContent.length <= pos) {
nodesBefore.push(node);
lengthCounter += node.textContent.length;
}
if(lengthCounter + node.textContent.length > pos) {
var ntBefore = node.textContent.substring(0, pos - lengthCounter);
var nBefore = document.createTextNode(ntBefore);
nodesBefore.push(nBefore);
var ntAfter = node.textContent.substring(pos - lengthCounter, node.textContent.length);
console.log(ntAfter);
var nAfter = document.createTextNode(nAfter);
nodesAfter.push(nAfter);
}
}
var nodeSet = [];
nodeSet.concat(nodesBefore);
nodeSet.push(elemToInsert);
nodeSet.concat(nodesAfter);
return nodeSet; // Is nu een array van de hele inhoud van het pre element
}
Maar dit werkt niet. Er komen dan steeds stukjes "undefined" in de tekst te staan, maar ik kan niet vinden wat er nou precies undefined is.
Gewijzigd op 20/07/2014 23:02:36 door Mark Hogeveen
Toevoeging op 20/07/2014 23:07:03:
Jij wilt dus een span in een span. dat maakt het inderdaad wel veel ingewikkelder omdat dit tot een 'oneindige' diepte kan. Hier zul je dan in ieder geval een recursive function voor moeten maken.
Gewijzigd op 20/07/2014 23:11:37 door Frank Nietbelangrijk
Ja! Wat Wouter heeft is precies wat ik wil. Allemaal bedankt voor jullie voorbeelden en hulp.