I am playing around with creating an HTML-textarea based plain text editor to edit my scripts (using e.g. Mozilla Prism + a localhost install/ webserver). It works fine so far, but when I want to insert something at the cursor position, it gets slow in Firefox when there is a lot of text in the textarea (Chrome works fine). E.g. with 133k filled in the textarea it takes around 1 sec to perform inserting 4 spaces.
I already have and use elm.selectionStart and elm.selectionEnd. Based on these I then copy the text, manipulate it, and set the value back into the textarea -- perhaps that is what's causing the bottleneck (I'm using the similar approach as answered on this site before). Ideally, I would probably like to have something like elm.selectedText = 'foobar' but can't find this...
It doesn't necessarily need to be crossbrowser...
Can someone help?
According to this article on codemirror, using designMode is faster than using a textarea, because you can edit parts of the content instead of editing the whole text in one go.
There's an API that replaces the selected text: textarea.setRangeText('text').
Here's a demo:
const textarea = document.querySelector('textarea');
textarea.addEventListener('click', () => {
textarea.setRangeText('WOW');
});
<textarea rows="10" cols="40">Click anywhere or select any text in here. It will be replaced by WOW</textarea>
There's also document.execCommand('insertText') with undo support but it's not cross-browser. Try insert-text-textarea for a cross-browser solution.
Related
By RTE tags I mean <b>, <i>, <u>, or <s> tags. I've been working on a RTE, and using jquery I can get the entire area to bold, but I want to be able to bold only a specific portion (think google docs, word, or any other text editor).
The mozilla site only had deprecated information, and inspecting elements on other sites (including this one) were no help to me.
I am trying to edit a content-editable <div> currently, although I'm open to switching to a text area if that works better.
//my jquery for bolding the entire thing
var bolded = false;
$("#bold").on('click', function(){
//access css of editor div, change status using a ternary
$("#editor").css("font-weight", bolded ? this.value : 'bold');
bolded = !bolded;
//log for debugging
console.log('clicked bold: ' + bolded);
});
my HTML5 for the editor. Sectioned off for formatting purposes.
<section>
<div id="editor" contenteditable="TRUE"></div>
</section>
My buttons are id'd as "bold", "itallic", "strike", and "underl", but I really just want to get one of them working so I can work from there.
EDIT
I realized that this question isn't as straightforward as I'd hoped. I have a <div>, and I would like to have multiple different formats inside of this <div>. The way I would do it logially is by inserting a <b> tag on the click of a button / keyboard command and then continuing to type from there, but I can't find any resources for it. Hope this clears that up.
EDIT 2
So as far as I can tell, the document.execCommand() still works but is predicted to be replaced by Input Events Level 2. I can't find any readable documentation for implementing this. Does anybody know how to do this?
Answer for current methodology (document.execCommand('command')):
Attaching a simple onclick() to the buttons works, where that onclick is a function that runs the aforementioned command with no particular focus:
function format(command, value){
//In use, "value" is left blank in order to do the current selection / no selection
document.execCommand(command, false, value);
}
<button onclick="format('bold')"><strong>B</strong></button>
<button onclick="format('italic')"><em>I</em></button>
Please note that this functionality WILL be deprecated, but no replacement has come up yet. When I know more, I will come back and edit this answer.
I'm turning here as a last resort. I've scoured google and I'm having troubles coming to a solution. I have a form with a textarea element that allows you to type html in the area and it will render the HTML markup live as you type if you have the preview mode active. Not too different from the way StackOverflow shows the preview below a new post.
However, I have recently discovered that my functionality has a vulnerability. All I got to do is type something like:
</textarea>
<script>alert("Hello World!");</script>
<textarea style="display: none;">
And not only does this run from within the textarea live, if you save the form and reload said data on a different page this code still executes within the textarea on said different page but unbeknownst to the user; to them all the see is a textarea (if there is no alert obviously).
I found this post; Live preview of textarea input with javascript html, and attempted to refactor my JS to the accepted answer there, because I noticed I couldn't write a script tag in the JSFiddle example, though maybe that's some JSFiddle blocking that behaviour, but I couldn't get it working within my JS file.
These few lines is what I use to live render HTML markup:
$(".main").on("keyup", "#actualTextArea", function () {
$('#previewTextArea').html($('#actualTextArea').val());
});
$(".main").on("keydown", "#actualTextArea", function () {
$('#previewTextArea').html($('#actualTextArea').val());
});
Is there a way this can be refactored so it's safe? My only idea at the moment is to wipe the live preview and use a toggle on/off and encode it, but I really think this is a cool feature and would like to keep it live instead of toggle. Is there a way to "live encode" it or escape certain tags or something?
In order to sanitise your text area preview simply replace all the < and > with their html character code equivalents:
function showPreview()
{
var value = $('#writer').val().trim();
value = value.replace("<", "<");
value = value.replace(">", ">");
$('#preview').html(value);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="writer" onInput="showPreview();">
</textarea>
<br/>
<hr/>
<div id="preview">
</div>
Edit: Actually, I think this solution is a little cleaner, and makes the below code unnecessary. In the velocity page all that is needed is to take advantage of the Spring framework. So I replace the textarea with this like so:
#springBindEscaped("myJavaObj.textAreaText" true)
<textarea id="actualTextArea" name="${status.expression}" class="myClass" rows="10" cols="120">$!status.value</textarea>
This paired with some backend Java validation and it ends up being a much cleaner solution.
But if you want a non-spring/ velocity solution, then this below works just fine
I cobbled together a quick fix as my main purpose is to eliminate the ability for others to execute scripts easily. It's not ideal, and I"m not claiming it to be the best answer, so if someone finds a better solution, please do share. I created a "sanitize" function like so:
function sanitize(text){
var sanitized = text.replace("<script>", "");
sanitized = sanitized.replace("</script>", "");
return sanitized;
}
Then the previous two event handlers now look like:
$(".main").on("keyup", "#actualTextArea", function () {
var textAreaMarkup = $('#actualTextArea').val();
var sanitizedMarkup = sanitize(textAreaMarkup );
$('#actualTextArea').val(sanitizedMarkup);
$('#previewTextArea').html(sanitizedMarkup);
});
// This one can remain unchanged and infact needs to be
// If it's the same as above it will wipe the text area
// on a highlight-backspace
$(".main").on("keydown", "#actualTextArea", function () {
$('#previewTextArea').html($('#actualTextArea').val());
});
Along with Java side sanitation to prevent anything harmful being stored in the DB, this serves my purpose, but I'm very open to a better solution if it exists.
I have a webpage div with contentEditable=true. After the user modifies the content of the div, the system uses document.getElementById(id).innerHTML to get the content and send it to server. This is all working:
<div id='editBox'; contentEditable='true'></div>
<script>
document.getElementById('editBox').onkeydown=function(e){
clearTimeout ( backupTimeoutID );
backupTimeoutID = setTimeout(function(){sendDivContentToServer()},3000);
}
<script>
the reason I'm using the a div with contentEditable=true instead of text area is that I need to allow displaying and formatting of background-colored text in the div. If I'm unaware of any better way, please, let me know.
My problem is with the inconsistence with which line breaks are displayed. If the user presses return inside the div, it creates another div for the line break. So, when the function gets the innerHtml, it looks like this
first line of text<div>second line of text</div>
Sometimes, when pasting text from other sources in the edit box (from internet, word, etc), line breaks appear as <p>.
I want all line breaks to look like this:
first line of text<br>second line of text
I have tried changing the behavior of the <div> whenever the user presses return; I know the code is working, for if I try to insert a word instead of return it works. But, when I set the code to substitute return for <br> it acts erradically. This is the code I'm using for this:
<script>
document.getElementById('editBox').onkeydown=function(e){
clearTimeout ( backupTimeoutID );
backupTimeoutID = setTimeout(function(){sendDivContentToServer()},3000);
}
} else if ( pressedKeyCode==13 ) {
e.preventDefault();
document.execCommand("InsertHTML",false,"a"); // this works
document.execCommand("InsertHTML",false,"<br>"); // I don't see the effects I want with this
}
<script>
Converting the multiple line breaks – <div>, <p> and <br> – seem to be a hard task. Using <br> for line breaks seems less error prone.
I'm developing for a web viewer in FileMaker for use in Mac OSX. So, so far, I care more about Safari than any other browser.
Thanks, in advance, for the help.
Reining in contentEditable is not an easy task. Unfortunately it's not really standardized and every browser has its quirks.
I would suggest you have a look at the many well written HTML rich text editors that are around.
For example, CKEditor only creates sensible, valid HTML, it allows you to configure what happens, when the user presses return, it can remove or replace any unwanted HTML and you can disable any features that the user shouldn't use.
When the user edits a contenteditable div, and press some keys, I would like to override the default behavior.
For instance, I want to insert a normal line break when the user press ENTER.
I do that using document.execCommand("insertText",...)
This is the only way I have found so far to make this action undoable and redoable by the user.
<div id="editor" contenteditable="true" style="white-space:pre-wrap">
Some text....
</div>
<script>
$("#editor").keydown(function(evt){
console.log(evt.keyCode);
if(evt.keyCode==13){
document.execCommand("insertText",false,"\n");
evt.preventDefault();
evt.stopPropagation();
}
}
</script>
This code works well on chrome and firefox. But, ie does not support "inserttext". Would there be a way to insert text with ie, such that the user can undo it?
IE 11 has new ms-beginUndoUnit and ms-endUndoUnit commands.
I tried and failed to create a working solution for IE 11 using these. Inserting a line break is hard with DOM ranges and selections; you can do it more easily in IE using its legacy document.selection and TextRange objects but unfortunately IE 11 removed document.selection (but not TextRange, strangely) which makes it difficult. After coding a nasty workaround to create a TextRange from the selection, undo didn't work anyway. Here's what I did:
http://jsfiddle.net/E7sBD/2
I decided to have another go using DOM Range and selection objects. The line break insertion code is illustrative and shouldn't be used for anything serious because it involves adding a non-breaking space to give the caret somewhere to go (trying to place it directly after the <br> does not work). Undoing does remove the inserted content but unfortunately doesn't move the caret.
http://jsfiddle.net/E7sBD/4/
You can always go the simpler and more stable way to catch the change event on the input div and change the last char from the input string to your liking.
http://api.jquery.com/change is invented i think for that purpose :)
Change your angel and you see a whole new world :3
I need to write a javascript function that can look at the WYSIWYG on the page (CKEditor) that is rendered after the page loads with the Drupal WYSIWYG module. I am having difficulty using jQuery even finding the editor.
$(textarea#textarea-id).change or .keyup do nothing.
I can do this:
console.log(CKEDITOR.instances);
That at least shows me the instance where CKEditor is attaching itself to. I can't seem to reference anything after that:
CKEDITOR.instances.myinstance-name or CKEDITOR.instances[0] both return undefined.
I've gone in circles for 2 hours now and not sure what else to try.
All I want to do is when the user is typing (keyup), count the characters input. If the input is greater than a certain length, I want to force a line break right in the text.
How can I go about implementing this? I thought it would be fairly straightforward.
Using other examples I have seen:
for (var i in CKEDITOR.instances) {
CKEDITOR.instances[i].on('change', function() {alert('test 1 2 3')});
}
Resulted in no alert.
The editor is being loaded/displayed in an iframe (via CKEditor/WYSIWYG in Drupal).
There's a plugin available that provides an onChange event for CKEditor, you can find it (along with instructions) here.
It suggests using code like this:
editor.on( 'saveSnapshot', function(e) { somethingChanged(); });