This regular expression is a white list for allowable characters in a text field. However, it doesn't catch the invalid characters when autocomplete is used to fill a field.
The shim at the end is used so IE and Edge can catch numpad invalid chars.
How can I get this regex to catch invalid chars when autocomplete fills?
var validChars = /^[ 0-9a-z\s.#,-]*$/i;
var textareas = document.querySelectorAll('.txt');
for(let i = 0; i < textareas.length; i++){
textareas[i].addEventListener("paste", function(e){
var clipboardData, pastedData;
// Get pasted data via clipboard API
clipboardData = e.clipboardData || window.clipboardData;
pastedData = clipboardData.getData('Text');
var inputOk = scrub(pastedData);
if(!inputOk){
e.preventDefault();
}
});
textareas[i].addEventListener("keypress", function(e){
var inputOk = scrub(e.key);
if(!inputOk){
e.preventDefault();
}
});
}
function scrub(contents)
{
if(contents.match(validChars))
{
return true;
}
else
{
alert("Invalid special character entered: " + contents + " ");
return false;
}
}
// KeyboardEvent shim needed for Internet Explorer and Edge. These browsers return non-standard 'key'
// property values from the numberpad.
(function() {
var event = KeyboardEvent.prototype
var desc = Object.getOwnPropertyDescriptor(event, "key")
if (!desc) return
var keys = {
Multiply: "*",
Add: "+",
Divide: "/",
}
Object.defineProperty(event, "key", {
get: function() {
var key = desc.get.call(this)
return keys.hasOwnProperty(key) ? keys[key] : key
},
})
})()
You can use add an input event listener to the textarea and only allow the input if it matches the regular expression.
<textarea></textarea>
<script>
var validChars = /^[ 0-9a-z\s.#,-]*$/i;
var prevInput = null;
document.querySelector('textarea').addEventListener("input", function(e){
if(!this.value.match(validChars)){
alert("Invalid Input!");
this.value = prevInput? prevInput: '';
}
prevInput = this.value;
});
</script>
Related
Our target browser is IE8 as the application is a legacy one and has got some com dependency.We are showing content inside a content-editable div. One of the requirements is to be able to replace texts inside the div when the browser is in "overwrite" mode. Paste is working fine but the input cursor is always moving to the end after the paste. We are using rangy-core, version: 1.3.1 for range/selection related logic. Unable to figure out what is going wrong here. Need help.
The following code is called when the document is loaded:
$("#info").on("paste", function (e) {
var clipboardData = window.clipboardData.getData("Text");
clipboardData = clipboardData.replace(/(^ *)|(\r\n|\n|\r)/gm, "");
if (isOverwriteEnabled()) {
e.preventDefault ? e.preventDefault() : e.returnValue = false;
pasteCopiedData(clipboardData);
}
});
Related code snippets for reference:
function isOverwriteEnabled() {
try {
// try the MSIE way
return document.queryCommandValue("OverWrite");
} catch (ex) {
// not MSIE => not supported
return false;
}
}
function pasteCopiedData(clipboardData) {
var json = getCurrentNodeWithOffset();
handleTextOverwrite(json, clipboardData);
}
function getCurrentNodeWithOffset() {
var json = {};
var selectedObj = rangy.getSelection();
var range = selectedObj.getRangeAt(0);
json.node = selectedObj.anchorNode.nodeType === 3 ? selectedObj.anchorNode : findImmediateTextNode(selectedObj.anchorNode, range.startOffset);
json.offset = selectedObj.anchorNode.nodeType === 3 ? range.startOffset : json.node.nodeValue.length - 1;
return json;
}
function handleTextOverwrite(json, textToReplace) {
var lenToCopy = textToReplace.length;
var offsetPos = json.offset;
var jsonNode = json.node;
try {
while (lenToCopy > 0) {
var toCopy = jsonNode.nodeValue.length - offsetPos;
var startPos = textToReplace.length - lenToCopy;
if (lenToCopy <= toCopy) {
json.node.nodeValue = jsonNode.nodeValue.substr(0, offsetPos) + textToReplace.substr(startPos) + jsonNode.nodeValue.substr(offsetPos + lenToCopy);
lenToCopy = 0;
}
else {
var copiedPos = startPos + toCopy;
jsonNode.nodeValue = jsonNode.nodeValue.substr(0, offsetPos) + textToReplace.substr(startPos, toCopy);
lenToCopy -= copiedPos;
var nextJsonNode = findNextTextNode(jsonNode);
if (!nextJsonNode) {
//do the remaining paste in jsonNode
jsonNode.nodeValue = jsonNode.nodeValue + textToReplace.substr(copiedPos);
lenToCopy = 0;
}
else {
jsonNode = nextJsonNode;
}
}
offsetPos = 0;
}
setCaret(json.node, json.offset);
}
catch (ex) {
}
}
function setCaret(node, pos) {
var el = document.getElementById("info");
var rangyRange = rangy.createRange(el);
var sel = rangy.getSelection();
rangyRange.setStart(node, pos + 1);
rangyRange.setEnd(node, pos + 1);
rangyRange.collapse(true);
sel.removeAllRanges();
sel.addRange(rangyRange);
}
Please let me know if more information is required.
I wrote the code below to set the first letter to upper case and set the data in CKEditor. The first letter is converting fine, but afterward, the cursor focus is set before the first letter. How can I set the cursor after the character?
CKEDITOR.on('instanceCreated', function (e) {
e.editor.on('contentDom', function () {
e.editor.document.on('keyup', function (event) {
var data = CKEDITOR.instances.editor1.getData();
var str = $(data).text();
var n = str.length;
if (str != null && n == 1) {
CKEDITOR.instances['editor1'].setData(titleCase(str))
function titleCase(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
});
});
});
this code is work for you :
$(document).ready(function() {
CKEDITOR.on('instanceReady', function(ev) {
ev.editor.focus();
var s = ev.editor.getSelection(); // getting selection
var selected_ranges = s.getRanges(); // getting ranges
var node = selected_ranges[0].startContainer; // selecting the starting node
var parents = node.getParents(true);
node = parents[parents.length - 2].getFirst();
while (true) {
var x = node.getNext();
if (x == null) {
break;
}
node = x;
}
s.selectElement(node);
selected_ranges = s.getRanges();
selected_ranges[0].collapse(false); // false collapses the range to the end of the selected node, true before the node.
s.selectRanges(selected_ranges); // putting the current selection there
});
});
I am using bootstrap-wysihtml5 (that is based on wysihtml5) amazing editor and it works fine and everyone is happy. But now I need to offer text suggestions when the user is typing something (like this or this). Those libraries expect a textarea and wysihtml5 uses an iframe with a <body contenteditable="true">.
All I need is some words to auto-complete (or open a popup with the suggestions) in some plain text inside the element.
Before I go deep in this, anyone has a suggestion of an library that could work inside an contenteditable="true" element?
Edit 1:
I created a basic lib that do what I need but I think I used all my poor js skills on this one... it works on a div with contenteditable=true but I'm having a hard time making it work on the wysihtml5 editor. Can some javascript/wysihtml5/rangy ninja give me some help?
Here is my code:
http://jsfiddle.net/5UQfH/
Edit 2:
First working version: http://jsfiddle.net/X9jBM/1/
Edit 3:
slightly better (but not prettier) version (works with multiple editors on same page):
http://jsfiddle.net/X9jBM/18/
Still do not work fine when the suggestion are multiple words (it stops suggesting when there is a space)
Would still like to hear some feedback on this.
I ended up creating a very simple and basic lib to do what I needed. It is not perfect nor tested in anything except the latest chrome version and I probably could eliminate the jQuery dependency quite easilly but since I already have it on my project (the old excuse), for now I will leave it as it is.
ENTER select a word and TAB cycle through the words.
http://jsfiddle.net/X9jBM/19/
The code:
if (typeof String.prototype.startsWith != 'function') {
String.prototype.startsWith = function (str) {
return this.indexOf(str) == 0;
};
}
var SuggestMe = function () {
"use strict";
var self = this;
return {
init: init
};
function init(iframe, words) {
self.list = [];
self.currentIndex = 0;
self.currentWord = "";
self.$iframe = iframe;
self.$editor = $(iframe).contents().find(".wysihtml5-editor");
self.$editor.on("keydown", function (event) {
if (event.keyCode === 13) {
var sel = rangy.getIframeSelection(self.$iframe);
if (!sel.isCollapsed) {
var range = sel.getRangeAt(0);
range.collapse(false);
var textNode = document.createTextNode(" ");
range.insertNode(textNode);
sel.collapseToEnd();
event.preventDefault();
return false;
}
}
if (event.keyCode === 9) {
var sel = rangy.getIframeSelection(self.$iframe);
if (!sel.isCollapsed) {
self.currentIndex++;
var word = self.list[self.currentIndex % self.list.length];
var sel = rangy.getIframeSelection(self.$iframe);
var range = sel.getRangeAt(0);
range.deleteContents();
var term = word.slice(self.currentWord.length, word.length);
var textNode = document.createTextNode(term);
range.insertNode(textNode);
range.selectNodeContents(textNode);
rangy.getSelection().setSingleRange(range);
event.preventDefault();
return false;
}
}
});
self.$editor.on("keyup", function (event) {
var c = String.fromCharCode(event.keyCode);
var isWordcharacter = c.match(/\w/);
if (isWordcharacter && !event.ctrlKey) {
var $editor = this;
self.currentWord = getLastWord($editor);
if (self.currentWord) {
var sel = rangy.getIframeSelection(self.$iframe);
if (sel.rangeCount > 0) {
self.list = [];
self.currentIndex = 0;
$.each(words, function (a) {
var word = words[a].toLowerCase();
if (word.startsWith(self.currentWord.toLowerCase())) {
self.list.push(word);
}
});
}
if (self.list.length) {
var word = self.list[self.currentIndex];
var sel = rangy.getIframeSelection(self.$iframe);
var range = sel.getRangeAt(0);
var term = word.slice(self.currentWord.length, word.length);
var textNode = document.createTextNode(term);
range.insertNode(textNode);
range.selectNodeContents(textNode);
rangy.getSelection().setSingleRange(range);
}
}
}
});
}
function getLastWord(elm) {
var val = elm.innerText.trim();
val = val.replace(/(\r\n|\n|\r)/gm, " ");
var idx = val.lastIndexOf(' ');
var lastWord = val.substring(idx + 1).trim();
console.log(val);
return lastWord;
}
};
The usage:
var suggestions = ["hello", "world", "blue dog", "blue cat"];
$('#txt').wysihtml5();
var editor = $('#txt').data("wysihtml5").editor;
editor.on('load', function () {
var sm = new SuggestMe();
sm.init(this.currentView.iframe, suggestions);
});
There is some refactor to be done but this is the basic idea.
i'm trying to make script which will replace all unwanted characters (from regexp) in input on keyup event. I tried everything but nothing works for me...
My code:
$('#form_accomodation_cell').on('keyup', 'input[name="accomodation_cell[]"]', function() {
var value = $(this).val();
var regex_cell = /^[0-9 \+]+$/g;
if (!isNumeric(value, regex_cell))
{
var new_value = value.replace(regex_cell, '');
alert(new_value);
}
function isNumeric(elem, regex_cell) {
if(elem.match(regex_cell)){
return true;
}else{
return false;
}
}
});
Try this:
$('#form_accomodation_cell').on("keyup", function () {
var value = $(this).val();
var regex_cell = /[^[0-9 +]]*/gi;
var new_value = value.replace(regex_cell, '');
alert(new_value);
});
See it here in action.
I think you should try to catch the event and finaly write this way !
function validateNumber(evt) {
var theEvent = evt || window.event;
var key = theEvent.keyCode || theEvent.which;
key = String.fromCharCode( key );
var regex = /^[0-9 \+]+$/g;
if( !regex.test(key) ) {
theEvent.returnValue = false;
if(theEvent.preventDefault) theEvent.preventDefault();
}
}
This question already has answers here:
How can I get the selected text in a textarea? [duplicate]
(6 answers)
Understanding what goes on with textarea selection with JavaScript
(4 answers)
Closed 10 months ago.
I'd like to store selected text in a variable and then delete the selected text by pressing a button. Preferably with jQuery, but I don't mind basic JavaScript.
I've tried the example that pointed to stripping down the code to what I need, but I can't get it to work on click for the button. It only works if I change #addchapter to textarea. what’s wrong with my code?
<html>
<head>
<script type="text/javascript" src="jquery-latest.pack.js"></script>
<script type="text/javascript" src="jquery-fieldselection.js"></script>
<script type="text/javascript"><!--//--><![CDATA[//><!--
$(document).ready(function(){
$('#addchapter').click(update);
});
function update(e) {
var range = $(this).getSelection();
$('#output').html(
"selected text:\n<span class=\"txt\">" + ((true) ? range.text.whitespace() : range.text) + "</span>\n\n"
);
}
String.prototype.whitespace = (function() {
if (!RegExp.escape) {
RegExp.escape = (function() {
var specials = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\' ];
var sRE = new RegExp( '(\\' + specials.join('|\\') + ')', 'g' );
return function(text) { return text.replace(sRE, '\\$1') }
})();
}
var ws = { "\r\n": "¶", "\n": "¶", "\r": "¶", "\t": "»", " ": "·" };
return ($.browser.msie) ? function() {
var s = this;
$.each(ws, function(i){ s = s.replace(new RegExp(RegExp.escape(i), 'g'), this) });
return s;
} : function () {
var s = this;
$.each(ws, function(i){ s = s.replace(new RegExp(RegExp.escape(i), 'g'), this + "\u200b") });
return s;
}
})();
//--><!]]>
</script>
</head>
<body>
<pre id="output"></pre>
<textarea id="area1" name="area1">textarea: foo bar baz</textarea>
<input type="button" value="add" id="addchapter">
</body>
</html>
I ended up using this - http://plugins.jquery.com/project/a-tools
The question seems clear enough but then the code is confusingly unrelated, so I'm going to answer the question as I understood it without the code.
The deleteSelectedText() function will delete the selected text from a <textarea> or <input type="text"> you provide and return you the text that was deleted.
function getSelectionBoundary(el, start) {
var property = start ? "selectionStart" : "selectionEnd";
var originalValue, textInputRange, precedingRange, pos, bookmark, isAtEnd;
if (typeof el[property] == "number") {
return el[property];
} else if (document.selection && document.selection.createRange) {
el.focus();
var range = document.selection.createRange();
if (range) {
// Collapse the selected range if the selection is not a caret
if (document.selection.type == "Text") {
range.collapse(!!start);
}
originalValue = el.value;
textInputRange = el.createTextRange();
precedingRange = el.createTextRange();
pos = 0;
bookmark = range.getBookmark();
textInputRange.moveToBookmark(bookmark);
if (/[\r\n]/.test(originalValue)) {
// Trickier case where input value contains line breaks
// Test whether the selection range is at the end of the
// text input by moving it on by one character and
// checking if it's still within the text input.
try {
range.move("character", 1);
isAtEnd = (range.parentElement() != el);
} catch (ex) {
log.warn("Error moving range", ex);
isAtEnd = true;
}
range.moveToBookmark(bookmark);
if (isAtEnd) {
pos = originalValue.length;
} else {
// Insert a character in the text input range and use
// that as a marker
textInputRange.text = " ";
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length - 1;
// Delete the inserted character
textInputRange.moveStart("character", -1);
textInputRange.text = "";
}
} else {
// Easier case where input value contains no line breaks
precedingRange.setEndPoint("EndToStart", textInputRange);
pos = precedingRange.text.length;
}
return pos;
}
}
return 0;
}
function deleteSelectedText(el) {
var start = getSelectionBoundary(el, true);
var end = getSelectionBoundary(el, false);
var val = el.value;
var selectedText = val.slice(start, end);
el.value = val.slice(0, start) + val.slice(end);
return selectedText;
}