Massive replacing in a contentEditable DIV - javascript

I've been searching about this a few days, but everything I found is with a selected text. I need to "bold" all the words within a contendEditable DIV that matches with "test".
I've been messing around getting the innerText from the DIV and change those words with the same with bold, but the cursor gets lost and when inerting again the changes with innerHTML, the cursor goes at the beginning. So I started searching doing those changes directly on the DIV, and the only thing I found its the execCommand, but at far as I know, you must select text first, and do the change, one by one. I would like to read all the DIV and make changes on the fly, is this posible? Is there a way to combine execCommand with regex?
Please, give me some light! :)

If you want to bold the words inside a contenteditable div, that is equivalent to placing <strong> tags around the given words.
So, I believe your problem could be solved with first defining a RegExp, with a 'g' flag for global
var boldText = "test"
var testRegEx = new RegExp(boldText, "g")
and then, where $div is the JQuery object representing your collection of divs (or the one) whose contents you want to modify:
$div.html($div.html().replace(textRegEx,"<strong>" + boldText + "</strong>"))

Related

Indesign find text not in whole document

I'm searching for a way to find the text of a specific paragraph style in an InDesign document but without searching the whole document, instead, I want to search only inside a text frame or other object.
The only way I found for now is using document.findText() and setting the findTextPreferences, is there something like myTextFrame.findText()?
You can use app.selection[0].findTEXT()
app.selection is an array that contains what you are selecting in Indesign.
And the [0] is the first item of the array.
So, you can select the textFrame and do your search.
Or
myTextFrame = app.selection[0];
myTextFrame.findText();
also works.
This only works on single text frame.
If you want to search in more than 1 textFrame,
you may need to write a for loop to do it.

Remove direct parent node of selection (<b>)

I would like to remove and add the <b> tag to a selected text just by clicking on it.
I can already enclose selected text in the <b> tag via:
if (window.getSelection)
{
selection = getSelection();
var newNode = document.createElement("b");
range.surroundContents(newNode);
}
Now that time I would like to have a method to remove the <b> tag I added beforehand, but the commonly shown method of using range.commonAncestorContainer would give me lowest parent of the <div> type.
As a part of debugging I simply modified the code I used to add the <b> tag (to make sure I had a tag, I also first added it) to remove the parent node.
if (window.getSelection) {
selection = getSelection();
var newNode = document.createElement("b");
range.surroundContents(newNode);
node = range.commonAncestorContainer;
node.remove();
}
This will remove the parent <div> of the newly added <b> tag, instead of only the <b> tag itself.
For reference, here is the HTML:
<div contenteditable="true" id="input" class="inputblock" onclick="getClick()" onkeypress="changeNodeType(event)" data-text="Please paste your text here."></div>
The <b> tag will be created within text pasted into the shown <div>.
The fact that it also removes the content can be ignored. I'll find a way to work with that.
I unfortunately don't have as much time as I'd like to spend to write out a full code solution, but this is how I've solved this in the past:
Normalize your range. This is the hardest part. IE, edge, and different browsers behave differently so you will need to handle a lot of special edge cases. For example if the range is right before an empty text node, IE will do wonky things
For your the start and endcontainer, if they are text nodes, do nothing
Otherwise, you need to use a NodeIterator to do an in-order traversal and walk to the next node for the start container, and the previous node for the end container. This solves the case where, when you select something like <b>hello</b>, the selection can either be |<b>hello</b>| or <b>|hello|</b>. By normalizing and walking down the tree, you will end up with a - mostly equivalent - selection of <b>|hello|</b> in both cases. It's hard to explain this without pictures, so I would encourage you to draw out the DOM tree and do a walk to the next innermost child of the start container and the previous innermost child of the end container.
Once you have nestled your way down, you can start to walk UP the tree to find a bold tag. This is simply node.contains('b');
Do that for both the start and end node, then see if you're pointing to the same bold tag, and that the text content is the same as your selection.
If so, you have found your bold tag to nuke.
It's a non-trivial amount of code, and it requires a bunch of testing due really wonky edge cases with setting the selection and range in browsers. I have fought contenteditable for a long time :-) and I wish you the best of luck

How to avoid breaking block level HTML elements from partial selection?

The problem in a nutshell is: given a wysiwyg editor (CKEditor) you want to make a plugin doing text transformation - select a piece of text and manipulate the text in it (eg uppercase). Example:
this is paragraph one
this is paragraph two
If bold represents your selection the result would be
this is paragraph ONE
THIS is paragraph two
This issue here is the selection will be a complete HTML fragment, even when a selection is no containing the full tag. The selected HTML is:
<p>one</p> <p>this</p>
Notice the first and last <p> tags. When you do your dom traverse in the selection html, apply the text transformation and replace the html it will use those partial tags, so your result become:
this is paragraph
ONE
THIS
is paragraph two
I checked if it's possible to "merge" the first and last partial tags with their dom parents, however the selection object is isolated, it doesn't have siblings or parents from it's original context.
Also tried to find an option to retrieve the selection without these auto-fixed tags, but no luck.
On the CKEditor documentation they mention a walker object - however that automatically expands from the selection to the full enclosing tag, which means the selection is only used as a minimum boundary.
Also because the selection object is isolated, it's not possible to just change the dom node text values there - the original dom fragment needs to be replaced (at least in case of CKEditor).
I tried not to stick with the CKEditor api as much as possible, however at this point I don't see any alternatives either. Is this is really a hard problem or I'm missing something?
One solution is to use the browser engine to mark the selected area with a tag (afaik that's a native operation). This is the same as you make your selection bold or italic - however here it's gonna be a temporary wrapper. Then you walk the DOM and replace the content in the temporary tags - and finally remove the tag (keeping the content).
This makes sure you can apply your transformation on the exact selection and by removing the tag you won't break the original DOM. Steps in a nutshell:
apply tag on selection (use the browser or wysiwyg api)
find all temp tags:
recursively walk the children of the tag
if tag is text node then apply transformation
otherwise recursive walk
collect tag's left sibling text node + tag's html + right sibling text node
replace tag's parent html with the previous compilation (=remove temp tag)
Not too elegant however works. This is inspired by #Andrew_Mast's idea about wrapping in a span.
I would loop through all of the word(s) and for each set of words inside a different tag, surround it with <span style="text-transform: uppercase;"> and </span>

Equivalent of selectionStart, selectionEnd, and focus() for contenteditable spans and divs

I am trying to set a contenteditable span so that it will on the onkeyup event refill the span with formatted text.
However, when I try this the cursor disappears and I can't type anymore. This is because the formatting program, instead of modifying the text already in the span, erases it all and then writes the formatted text in its place.
It is important (I think it is) to note that the element does not appear to be losing focus. Also, because I would like for this project to remain fairly "secret" until it's release, I would rather not give away the source code right now.
This is probably due to you using the innerHTML to set the formatted text.
Instead use childNodes collection to access the content and replace textNodes with formatted html element. This avoids setting innerHTML and avoids loosing focus.

how to use js to do very basic syntax coloring?

here's a very simple js but i don't know where to begin.
in a html page, if some text is enclosed by angle brackets, like this:
〈some text〉
i want the text to be colored (but not the brackets).
in normal html, i'd code it like this
〈<span class="booktitle">some text</span>〉
So, my question is, how do i start to write such a js script that search the text and replace it with span tags?
some basic guide on how to would be sufficient. Thanks.
(i know i need to read the whole html, find the match perhaps using regex, then replace the page with the new one. But have no idea how that can be done with js/DOM. Do i need to traverse every element, get their inner text, do possible replacement? A short example would be greatly appreciated.)
It depends partially on how cautious you need to be not to disturb event handlers on the elements you're traversing. If it's your page and you're in control of the handlers, you may not need to worry; if you're doing a library or bookmarklet or similar, you need to be very careful.
For example, consider this markup:
<p>And <a href='foo.html'>the 〈foo〉 is 〈bar〉</a>.</p>
If you did this:
var p = /* ...get a reference to the `p` element... */;
p.innerHTML = p.innerHTML.replace(/〈([^〉]*)〉/g, function(whole, c0) {
return "〈<span class='booktitle'>" + c0 + "</span>〉";
});
(live example) (the example uses unicode escapes and HTML numeric entities for 〈 and 〉 rather than the literals above, because JSBin doesn't like them raw, presumably an encoding issue)
...that would work and be really easy (as you see), but if there were an event handler on the a, it would get blown away (because we're destroying the a and recreating it). But if your text is uncomplicated and you're in control of the event handlers on it, that kind of simple solution might be all you need.
To be (almost) completely minimal-impact will require walking the DOM tree and only processing text nodes. For that, you'd be using Node#childNodes (for walking through the DOM), Node#nodeType (to know what kind of node you're dealing with), Node#nodeValue (to get the text of a text node), Node#splitText (on the text nodes, to split them in two so you can move one of them into your span), and Node#appendChild (to rehome the text node that you need to put in your span; don't worry about removing them from their parent, appendChild handles that for you). The above are covered by the DOM specification (v2 here, v3 here; most browsers are somewhere between the two; the links in the text above are to the DOM2 spec).
You'll want to be careful about this sort of case:
<p>The 〈foo <em>and</em> bar〉.</p>
...where the 〈 and the 〉 are in different text nodes (both children of the p, on either side of an em element); there you'll have to move part of each text node and the whole of the em into your span, most likely.
Hopefully that's enough to get you started.
If the text could be anywhere in the page, you have to traverse through each DOM element, split the text when you found a match using a regex.
I have put my code up there on jsfiddle: http://jsfiddle.net/thai/RjHqe/
What it does: It looks at the node you put it in,
If it's an element, then it looks into every child nodes of it.
If it's a text node, it finds the text enclosed in 〈angle brackets〉. If there is a match (look at the first match only), then it splits the text node into 3 parts:
left (the opening bracket and also text before that)
middle (the text inside the angle bracket)
right (the closing bracket and text after it)
the middle part is wrapped inside the <span> and the right part is being looked for more angle brackets.

Categories