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.
Related
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>
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 replacing things in my chat room it comes up in the box as the 'HTML Character Entities'. However, I want it to revert back and actually show the character typed in when it is then shown in the chat room. So I am using the following code to stop any html from being entered and damaging the chat room by replacing certain html character with there entities (I want to get one or two working before I look at the others I know there are many more.) ....
Javascript
var str1 = this.value.replace(/>/g, '<');
if (str1!=this.value) this.value=str1;
var str2 = this.value.replace(/</g, '>');
if (str2!=this.value) this.value=str2;
and then the following code then displays the text after it has been entered into the database etc. and on updating the chat box it uses the following to add in the the updated messages ...
Returned from php and then displayed through the following javascript
$('#chatroomarea').append($("<p>"+ data.text[i] +"</p>"));
I have messed around with this a few times changing it to val and using
.html(.append($("<p>"+ data.text[i] +"</p>")));
Etc. But I have had no luck. I'm not quite sure how to do this I just need the HTML Character Entities to actually show up back in there true Character instead of displaying something such as... '>'
This might be something I need to actually put within the replacing code where it will include code of it's own on replacing such as (this is just an example I'm not exactly sure on how I would write it) ....
var str1 = this.value.replace(/>/g, '.html(<)');
Any help on this would be much appreciated, Thank you.
$('#chatroomarea').append($("<xmp>"+ data.text[i] +"</xmp>"));
HTML xmp tag
The use is deprecated, but supported in most browsers.
Another option will be to use a styled textarea , To my knowledge these two are the tags that doesn't bother rendering html tags as it is.
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.