I have a text in the following format:
<div id="text">
<div>Hello world</div>
<div>How are you</div>
</div>
User selects the "w" of world:
So I get the selection and can insert the span:
var selection = window.getSelection();
var startNode = $(selection.anchorNode.parentElement);
var endNode = $(selection.focusNode.parentElement);
var startIndex = startNode.index();
var endIndex = endNode.index();
var startOffset = selection.anchorOffset;
var endOffset = selection.focusOffset;
Result:
<div id="test">
<div>Hello <span class="id1">w</span>orld</div>
<div>How are you</div>
</div>
id1.startNode = 0
id1.endNode = 0
id1.startOffset = 6
id1.endOffset = 7
The user user now selects "d Ho" of the text. This is how it should look like:
<div id="test">
<div>Hello <span class="id1">w</span>orl<span class="id2">d</span></div>
<div><span class="id2">Ho</span>w are you</div>
</div>
id2.startNode = 0
id2.endNode = 1
id2.startOffset = 9
id2.endOffset = 2
But using the method from before selection.anchorOffset yields me the index relative to the new created text nodes which are now "Hello ", "w" and "orld" which means I get an index of 3 instead of 10.
I use the following code to get the offset that was created:
var offs = 0;
if(startNode.context.innerHTML.length > startNode.context.innerText.length) {
var n = startNode.context.childNodes;
for (var i = 0; i < n.length; i++) {
if(n[i].textContent === selection.anchorNode.textContent) {
break;
}
else {
offs += n[i].textContent.length;
}
}
}
console.log(offs);
I can now add the offset to the start and end but this kind of breaks when the selection goes over different nodes or contains a selection inside of it. I also have no idea how to generate the spans with those offsets. There are also way to many cases that I have to consider like startNode === endNode or if a selection contains a span fully or partly.
Are there any common approaches or frameworks for stuff like this? I would rather just work with pure text indices instead of html tags inside and let something else handle the proper formatting.
At the risk of self-promotion, my Rangy library has a Class Applier module specifically for this.
Related
I have a javascript string that contains some code like [Hover on me](Tooltip text). I want to convert this code to HTML. [] containing text and () containing tooltip text
Here is what I want
<div class="tooltip">Hover over me
<span class="tooltiptext">Tooltip text</span>
</div>
The string contains multiple tooltip codes and I need to convert each of them to this HTML code
what I am trying
<p id="p">blabla [some info](tool) it is text [one](tool) [two] (tool)</p>
var text = document.getElementById('p').innerHTML;
var matches = text.match(/\[(.*?)\]/);
var final = text.match(/\((.*?)\)/);
var res = text.replace(matches[0], "<span>"+matches[1]+"</span>");
var newtext = document.getElementById('p').innerHTML = res;
var ros = newtext.replace(final[0], "<span>"+final[1]+"</span>");
document.getElementById('p').innerHTML = ros;
console.log(ros);
I know the code is pretty messed up but I need some help with this. I was also trying other code as well but that didn't work either, here is fiddle https://jsfiddle.net/mohitchandel/aszv8w93/10/
var text = document.getElementById('p').innerHTML;
// You may want to validate this loop better.
while( text.indexOf(')') > 0 ) {
let a = text.slice( text.indexOf('[') + 1, text.indexOf(']') ),
b = text.slice( text.indexOf('(') + 1, text.indexOf(')') ),
c = `<div class="tooltip">${ a } <span class="tooltiptext">${ b }</span></div>`;
text = text.slice( 0, text.indexOf('[') ) + c + text.slice( text.indexOf(')') + 1 );
}
This could get you started.
--
RMZ
I have a form that has multiple fields all with the same class. These are populated with URL's that follow the same structure. I am trying to extract the same section from each URL. So far var res = x.split('/')[5]; will achieve this but only for the first URL. I can also use var x = document.querySelectorAll(".example") to change all the url's but I cannot find the correct way to combine both of these function. so far my code looks like this:
script>
function myFunction() {
var x = document.querySelectorAll(".example").innerHTML;
var res = x.split('/')[5];
var i;
for (i = 0; i < x.length; i++) {
x[i].innerHTML = res;
}
}
</script>
I have looked around but can't find a solution that fits. Thanks in advance for your help.
So loop over the HTML Collection, this is making assumptions based on code.
// Find all the elements
var elems = document.querySelectorAll(".example")
// loop over the collection
elems.forEach(function (elem) {
// reference the text of the element and split it
var txt = elem.innerHTML.split("/")[5]
// replace the text
elem.innerHTML = txt
})
<div class="example">1/2/3/4/5/a</div>
<div class="example">1/2/3/4/5/b</div>
<div class="example">1/2/3/4/5/c</div>
<div class="example">1/2/3/4/5/d</div>
<div class="example">1/2/3/4/5/e</div>
<div class="example">1/2/3/4/5/f</div>
I'm trying to remove the excessive closing tags in javascript and anything that follows after that.
Here is a possible sample:
<div class="dummy">
<div class="main">
<div></div>
<img src="a.jpg">
<br>
<img src="b.jpg />
<strong>
<span>text</span>
</strong>
</div>
</div>
***excessive tags below***
</div>
</div>
<div class="footer">
text
</div>
</body>
</html>
Any ideas about how to do it efficiently?
The part I want to extract is always a div, but the problem is that it may have as many nested divs, and I'm not sure how to deal with this scenario.
If it can be done in multiple steps or with callbacks is also fine, as long as it works.
Edit
My question is actually easier than it seems.
The sample always starts with the div that I want to extract.
So all I need is to find the matching closing tag, and filter anything that follows.
Don't care about any other tags...
Don't use regex, from my understanding, you want to retain the dummy class div and the footer class div so why not replace the body with that?
E.g.
var dummy = document.getElementsByClassName('dummy')[0];
var footer = document.getElementsByClassName('footer')[0]
var body = document.getElementsByTagName('body')[0];
body.innerHTML = '';
body.appendChild(dummy);
body.appendChild(footer);
https://jsfiddle.net/1kq11ry2/
data='<div class="dummy"><div class="main"><div></div><img src="a.jpg"><br><div></div><img src="b.jpg /><strong><span>text</span> </strong></div><div><div></div></div><div><div></div></div></div>***excessive tags below***</div></div><div class="footer">text</div></body></html>';
var starting_tags = [];
var closing_tags = [];
var startIndex, index=0;
var searchStrLen = 4;
while ((index = data.indexOf('<div', startIndex)) > -1) {
starting_tags.push(index);
startIndex = index + searchStrLen;
}
index,startIndex=0;
searchStrLen = 6;
while ((index = data.indexOf('</div>', startIndex)) > -1) {
closing_tags.push(index);
startIndex = index + searchStrLen;
}
var nest_level=0;
for (var i=0; i<closing_tags.length && nest_level<closing_tags.length && nest_level<=closing_tags.length; ++i) {
for (var j=0+nest_level; j<starting_tags.length; ++j) {
if (starting_tags[j]<closing_tags[nest_level])
nest_level++;
}
}
result = data.substr(startIndex[starting_tags], closing_tags[nest_level-1]+6);
console.log(nest_level);
console.log(starting_tags);
console.log(closing_tags);
console.log(result);
I was able to solve it. The code above calculates the level of div nesting, and then chops it off if it finds excessive closing tags.
https://jsfiddle.net/89j7yakz/2/
<div id = "board>
<div>{abc</div>
<div>def</div>
<div>ghi}</div>
</div>
I want compare every char inside the div at their position and do something when if found { or }
Im aware that this is possible by wrapping every char within <span></span>
Is there a way to do this without using a span? I will use this for brace matching of my code editor project. this is what i've done using span wrapping, but it is so slow..
$exceedingInlineDiv = $('#board_code_dup > div').eq(x);
if( $exceedingInlineDiv.text() == ''){
var chars = '<span> <br> </span>';
$exceedingInlineDiv.html(chars);
}
else{
var chars = jQuery.map($exceedingInlineDiv.text().split(''), function(c) {
return '<span>' + c + '</span>';
});
$exceedingInlineDiv.html(chars.join(''));
}//else
I'm not sure what you want to do, but maybe you want something like this:
var board = document.getElementById("board"),
divs = board.getElementsByTagName("div"),
texts = [], i = 0;
for (; i < divs.length; i++)
texts.push(divs[i].innerHTML);
// texts => ["{abc", "def", "ghi}"]
If I have two elements :
Element A :
<div id="myID">
<div id="a"></div>
<div id="b"><div id="ba"></div></div>
<div id="c"><span id="ca"></span></div>
</div>
and Element B :
<div id="myID">
<div id="a"></div>
<div id="b"><div id="ba"></div></div>
<div id="c"><span id="ca"></span></div>
<div id="d"></div>
</div>
Is it possible to find out that Element B has more children than Element A, then find where is additional element and create it in Element A?
P.S: In real code new element is loaded with Ajax Request, but I don't want to replace all content with loaded content, I need to add only new content and skip existing one.
P.S.S : In my current code I have Md5 checksum to check if new content is not the same as existing, but if new content have only little changes it replaces all content and this is the problem for me.
A piece of my current code :
window.processResponse = function(data) {
// Note : "data" is Ajax responseText;
if(!data) return false;
var $data = document.createElement("div");
$data.innerHTML = data;
var em = $data.getElementsByTagName("*");
for(var i = 0; i < em.length; i++)
{
var parent = sget(em[i].id); // sget function is : document.getElementById
if(parent)
{
var html = em[i].innerHTML.replace(/(\s)+/gim, "").replace(/(\n|\r\n)+/gim, "");
var id = em[i].id;
savedPages[id] = savedPages[id] || [];
var _md5 = md5(html);
if(savedPages[id][0] == _md5) continue;
savedPages[id] = [_md5, getTime()];
parent.innerHTML = em[i].innerHTML;
}
if(em[i].tagName === "SCRIPT")
{
var code = em[i].innerHTML.replace(/(\s)+/gim, "").replace(/(\n|\r\n)+/gim, "");
var id = em[i].id;
savedPages[id] = savedPages[id] || [];
var _md5 = md5(code);
if(savedPages[id][0] == _md5) continue;
savedPages[id] = [_md5, getTime()];
try{eval(em[i].innerHTML)}catch(ex){log(ex)};
}
}
};
So, you can optimize it but it depends also in which browser are you running this code.
I assumed the follows:
All IDs are unique, and you rely on that. You want to compare basically elements that have the same ID, not the same structure.
As you said, all children have IDs, and you want to compare only children – not nested node
The elements received from the server have only additional children not less. For removing children, you have to add some other code.
Therefore, if you have the same number of children, we assume they're the same (to optimize). If this is not true, then you have to implement the removal of children as well
Said that, I believe that this kind of stuff is more suitable on server side, that should send to the client only the part that are actually modified. That what we do usually – or, if we don't care, replace everything.
var div = document.createElement('div');
div.innerHTML = s;
var root = div.firstChild;
var children = root.children;
var documentRoot = document.getElementById(root.id);
if (documentRoot && documentRoot.children.length < children.length) {
var node = null;
var previousNode = null;
var index = 0;
while ( node = children[index++] ) {
var documentNode = document.getElementById(node.id);
if (!documentNode) {
if (previousNode)
documentRoot.insertBefore(node, previousNode.nextSibling);
else
documentRoot.insertBefore(node, documentRoot.firstChild);
documentNode = node;
}
previousNode = documentNode;
}
previousNode = null;
} else {
// probably append as is somewhere
}
The solution is not so simple. What if the parent, myID, did not exist in sample A but the child nodes were in both samples indicating 3 layers in the DOM that need to be compared and adjusted? How would you compare this:
<div id="papa">
<div id="myID">
<div id="a"></div>
<div id="b">
<div id="ba"></div>
</div>
<div id="c">
<span id="ca"></span>
</div>
</div>
</div>
vs
<div id="papa">
<div id="a"></div>
<div id="b">
<div id="ba"></div>
</div>
<div id="c">
<span id="ca"></span>
</div>
</div>
In this case the comparison becomes more complicated. You will actually need a fully fleshed out XML/HTML language aware diff utility with a merge function. You can play around with Pretty Diff to demonstrate just how complicated this can get, but unfortunately it does not have a merge function so it cannot be a fully automated solution to your problem.