Highlight/Format textareas - javascript

I'm looking to implement a live diff inside a webpage that preserves whitespace.
I've done something similar in the past using a combination of TinyMCE and JQuery in IE6 (required for that project) but it didn't preserve whitespace. (It wasn't supposed to, but I didn't add anything to make it do that, it just did). TinyMCE wasn't ideal as I was dealing with plain text, and TinyMCE is a WYSIWYG style editor supporting rich text. Most of it's functionality wasn't being used and is/was hard to disable.
I've looked through most of the projects listed at http://en.wikipedia.org/wiki/Comparison_of_JavaScript-based_source_code_editors. EditArea seemed promising, but I cannot seem to determine which elements are actually being used to display the text.
$('#frame_modified').contents().find('#editor').text()
displays a lot of the line information (123456789101112 and so on) along with the line the cursor is under, but not the rest. I haven't figured out how to apply/modify the styles associated with that.
Lets say the user types foo, I'd like to wrap it so it displays as <span class="bar">foo</span> on the fly. What's the simplest solution to go about getting this sort of functionality?
IE8 is the target browser for this project.

You could do some action on the onKeyDown event (in one of your own plugins). If your cursor is already in a new span add the character just typed in else create a new span with the character entered. Something like this:
ed.onKeyDown.add(function(ed,evt){
char = getChar(evt.keyCode); // your function to get the character to be inserted depending on evt.keyCode
// if span already exists
node = ed.selection.getNode();
if (node.nodeName.toLowerCase() == 'span' && node.className == 'myclass'){
node.innerHTML += char;
}
// new span
else {
doc = ed.getDoc();
new_element = doc.createElement('span');
new_element.className = "myclass";
new_element.innerHTML = char;
tinymce.activeEditor.mceInsertContent(new_element);
}
})

Related

Wrappable one-line text input

I am writing an browser-based text editor for one of my projects. Each text has a title, which the user can edit. The user should not be able to format that title or add any html to it, it should just be a plain string.
A <input type="text"> element would be ideal, but since titles can be very long I need line wrapping, which is something that the input tag cannot do, as far as I know.
I could use a <textarea>, but that would allow the user to add line breaks manually, which I don't want.
I could use <p contenteditable="true">, but that can add unwanted markup and the user would be able to insert manual line breaks and other markup.
I could write a whole bunch of JavaScript to validate and restrict a <textarea> or contenteditable tag, but that seems to be very error-prone and could introduce cross-browser inconsistencies. Or am I making this way too complex and there is an easy way to prevent misuse?
I'm sure people have solved this problem before. What am I missing here? Do I really need a massively complex JavaScript solution to have a wrappable one-line text input?
Incidentally, I'm using tiptap as my editor component. I know that it can do title inputs but so far I haven't figured out how to extract that title. I want it to be stored separate from the text, not as part of the text. If anybody has inputs regarding using tiptap to solve my problem, that would also qualify as an answer - although I think it's a bit overkill to use a full-fledged richtext editor for a simple, unformatted one-liner. I would prefer more lightweight solutions.
Here are two simple solutions :
Disable Enter key on client side
document.querySelector('#textInput').addEventListener('keydown',
function(event) {
var keyCode = event.keyCode || event.which;
// If ENTER key
if (keyCode === 13) {
event.preventDefault(); // do nothing
}
}
);
<html>
<body>
<textarea id="textInput" rows=3></textarea>
</body>
</html>
Remove line breaks on server side
let text = inputText.replace(/\n/, '');
Demo
Or replace them with a space then replace double spaces with one
let text = inputText.replace(/(\r\n|\n|\r)/gm, ' ').replace(/\s+/g, ' ');
Demo
Using both solutions is better than only one.

How to add <span> within CodeMirror editor?

No jQuery please!
I'd like to add a styled span within the body of a CodeMirror editor. This is for the purposes of a mail merge like application where you can for instance do: (From Zapier)
I believe this may be possible using a CodeMirror Widget but I'm lost as to what direction to go in. I found an example of something similar (albeit far more complicated) here.
I also tried tagify which is extremely similar to what I'm after but doesn't have multi-line inputs, which is a requirement.
All I need is the ability to add and remove (via backspace with the caret just behind the tag) these spans but there doesn't appear to be a simple solution.
Also if there is a convenient library or some other direction I can go in not involving CodeMirror that'd also be fine.
CodeMirror is actually well suited for this.
First insert some placeholder into the document, such as [[tag]].
var lineNumber = 0;
var charNumber = 0;
var snippet = "[[tag]]"
editor.doc.replaceRange(snippet, {line:lineNumber, from: charNumber});
Then create your DOM element, I recommend a span.
var htmlNode = document.createElement("span");
//Style and add what you like to the span
Then use doc.markText to replace it with that node.
editor.doc.markText({line: lineNumber,ch: charNumber}, {line: lineNumber,ch: charNumber + snippet.length}, {
replacedWith: htmlNode
})

HTML / Javascript Automatic ContentEditable Tags?

I'm trying to build a basic browser based text editor for a diary, and I was wondering if anyone could point in the right direction of how to set up ContentEditable tags?
At the moment I've been following this guide here:
https://www.simonewebdesign.it/how-to-make-browser-editor-with-html5-contenteditable/
And have it working, however it requires you to enter the file, actually type something like <h1>TEST</h1> before you can then reopen the file and edit the aforementioned text.
I'm looking for a way to type in <h1> and the browser recognise that and turn the following text into a header, same thing for <p> too.
So to summarise I want to be able to do the following:
Open the HTML file, write <h1> and then the browser recognises I'm typing a header, <p> and then the browser recognises I am now typing a paragraph.
Any help is most welcome, I'm still quite new at this and I'm assuming it's going to require Javascript.
This could get pretty tricky, since you'd have to basically monitor every change to the editable area, and because HTML tags enclose their content - so updating on the fly means setting invalid HTML. Here's a rough bit of code that sort of does what you're describing, but it's got a lot of constraints and isn't the most user-friendly experience. Still, feel free to build from it!
var area = document.querySelector('#edit-me');
var tagOpen = false;
area.addEventListener('keyup', function(e) {
// Check for ">", aka period + shift
if (e.keyCode == 190 && e.shiftKey) {
// We toggle this flag so it doesn't convert the text to html
// until the tag seems closed - ie until two '>'s have been entered.
// This will work for simple one-level html,
// but will have issues with self-closing tags and nesting.
tagOpen = !tagOpen;
if (!tagOpen) {
var areaContent = area.textContent;
area.innerHTML = areaContent;
}
}
});
<div id="edit-me" contenteditable>Click to edit</div>

Iterating over all tags in a string containing HTML using javascript/jquery

I'm using a rich text editor type control, which is a written as a jQuery plugin. It basically inserts an IFrame onto the page, and makes it editable - fairly standard for rich text controls.
Now, what I'm looking to do is improve upon an option which removes all formatting from the text editor. Currently it is being done with a large list of regular expressions, and a quick google search suggests that this is not the correct way to go about it. I'm looking to allow this unformatting some degree of flexibility, so that I can leave certain tags in (like paragraph tags).
I was trying to use the jQuery built in DOM parsing to do this easily, but I seem to be having trouble.
Let's assume I have a sample HTML string:
<Body><p>One <strong>Two</strong> <em>Three</em></p></Body>
I'm looking to un-format it so that all non paragraph tags are removed. So, I'd be expecting the output to be a string which looks like this:
<Body><p>One Two Three</p></Body>
Sample code:
//Some very simple HTML obtained from an editable iframe
var text = '<Body><p>One <strong>Two</strong> <em>Three</em></p></Body>';
var $text = $(text);
//All tags which are not paragraphs
$(':not(p)',$text).each(function() {
//Replace the tag + content with just content
$(this).html($(this).text());
});
//I'll be honest, I found this snippet somewhere else on stackoverflow,
//It seems to parse the jquery object back into an HTML string.
var returnVal = "";
$text.each(function(){
returnVal += $(this).clone().wrap('<p>').parent().html();
});
//Should be equal to '<p>One Two Three</p>'
return returnVal;
This seems like it should work, but unfortunately it doesn't. In the above example, 'returnVal' is the same as the input (minus the 'body' header tags). Is there anything I'm obviously doing wrong here?
Replace this line:
$(this).html($(this).text());
... with this:
$(this).replaceWith($(this).text());
... and it should work (at least it works here).
...snip
// Here's your bug:
$(':not(p)',$text).each(function() {
// You can't use .html() to replace the content
// $(this).html($(this).text());
// You have to replace the entire element, not just its contents:
$(this).replaceWith($(this).text());
});
...snip

JavaScript - How to get the current selection 'index' within a document

My code here returns a JavaScript selection object from within an iFrame (the iFrame page is within the same domain, so no xss issue).
What I need to know is the index of the selection within the raw html code (not the dom).
UPDATE:
E.g.
If you have an html doc:
<html><body>ABC</body></html>
And in the UI, the user uses their mouse to select the text 'ABC', I want to be able to use JavaScript to determine the postion of the selected text in the html source. In this case the index of ABC is 13.
UPDATE 2
The reason I'm persisting with this madness, is that I need to create a tool that can revisit a page and pull text based on a selected text the user has identified at an earlier time. The user tells the system where the text is, and the system from that point on uses regular expressions to pull the text. Now, if the dom is not the same as the raw html, and there's no way to pinpoint the selection in the raw html - it's really difficult to know what reg ex to generate. I don't think there's another way around this.
// Returns the raw selection object currently
// selected in the UI
function getCurrentSelection() {
var selection = null;
var iFrame = document.getElementById('uc_iFrameGetPriceData');
try {
if (window.getSelection) { // Gecko
selection = iFrame.contentWindow.getSelection();
}
else { // IE
var iframeDoc = iFrame.contentWindow.document;
if (iframeDoc.selection) {
selection = iframeDoc.selection;
}
else {
selection = iframeDoc.contentWindow.getSelection();
}
}
}
catch (err) {
alert( 'Error: getCurrentSelection() - ' + err.description )
}
return selection;
}
You can access the index and offset of your selection by using selection.anchorOffset and selection.focusOffset.
Take a look at this:
http://help.dottoro.com/ljjmnrqr.php
And here's another well explaned article:
http://www.quirksmode.org/dom/range_intro.html
update to your update: I'm not sure why you're trying to get the index of the raw HTML code. But you can walk the DOM based on the selection kinda like this:
selection.anchorNode.nodeValue.replace(selection.anchorNode.nodeValue.substring(selection.anchorOffset, selection.focusOffset), 'replace value')
Note that it's still possible that anchorOffset is before focusOffset, based on whether you selected the text from left to right or from right to left.
If I understand correctly, you're looking to move around in the DOM. In that case, you can use these methods/properties:
parentNode
getChildNodes()
firstChild and lastChild
...and these links might help:
http://www.codingforums.com/archive/index.php/t-81035.html
http://www.sitepoint.com/forums/showthread.php?t=586034
The fastest way is probably
var node = document.getElementById('myElement');
alert(node.parentNode.indexOf(node));
(Sorry, for some reason the formatting buttons aren't showing up in my "Your Answer" area...)
I would be surprised if that information was available.
No DOM API is going to let you distinguish between
<html><body>ABC</body></html>
and
<html ><body >ABC</body></html>
The index in the raw HTML is different in each case, but the constructed DOM is identical.
You can't do this sensibly: the only possible method is to re-download the page's HTML via Ajax, parse the HTML and match the resulting DOM against the current DOM, which may itself have been altered by JavaScript. Besides, it's not a useful number anyway because once the page has been loaded, the original HTML string simply no longer exists in the DOM so offsets within that string have no meaning in JavaScript. Getting the selection in terms of nodes and offsets is much more sensible.

Categories

Resources