UPDATE
Issue is not with e.key === " " but with the inner if statement, to clarify. The inner if always returns false no matter what. The outer if is fine. So far, we think that this might be an issue with .innerText. It could be returning a different whitespace character at the end.
I think I might have found an internal bug with Javascript or something because this does not work. I'm trying to prevent the user from inserting multiple spaces in a row in a content editable div using Javascript. User should be allowed to enter one space at a time.
This is allowed
This is not
Here's what I'm trying:
document.getElementById("div")
.addEventListener("keydown", function(e) {
if (e.key === " ") {
const text = this.innerText,
i = window.getSelection().anchorOffset;
const chars = text.substring(i - 1, i + 1);
if (chars.includes(" ")) {
e.preventDefault();
return false;
}
}
});
<div contenteditable=true id=div></div>
The logic seems correct to me. The problem is that it doesn't work. I can insert as many spaces as I want to and the browser doesn't care. I have logged the variables using a debugger at each step and it has the expected value, but when chars.includes(" ") is evaluated, the if statement always evaluates to false for some reason even though chars contains a (space). Am i doing something wrong? please help.
Because of how contenteditable elements work, spaces you type are non-breaking spaces (ASCII 160) instead of normal spaces (ASCII 32). HTML collapses multiple spaces to one by default so spaces must be non-breaking inside the contenteditable, otherwise you wouldn't see more than one when typing multiple spaces.
You'll have to check for a non-breaking space specifically:
document.getElementById("div")
.addEventListener("keydown", function(e) {
if (e.key === " ") {
const text = this.innerText,
i = window.getSelection().anchorOffset;
const chars = text.substring(i - 1, i + 1);
if (chars.includes( String.fromCharCode(160) )) {
e.preventDefault();
return false;
}
}
});
<div contenteditable=true id=div></div>
You might also want to check for normal spaces as well because I'm not sure if this behavior is consistent among browsers.
ok, you can't put 2 spaces anywhere
edit: works in chrome, ff, edge
document.getElementById('div').addEventListener('keydown', function(e) {
if (e.code === 'Space' || e.key === ' ') {
const node = window.getSelection();
const text = node.anchorNode.textContent;
const cur = node.anchorOffset;
const front = text[cur - 1];
const back = text[cur];
const reg = /\s/;
if (reg.test(front) || reg.test(back)) e.preventDefault();
}
});
div {
border: 1px solid tomato;
}
<div contenteditable="true" id="div"></div>
First, add a keydown listener to check what the last key pressed was like this:
document.getElementById("div").addEventListener("keydown", function(e) {
});
Second, Within that listener function, add another function, say stop() that has all the codes that you want to run (e.g. preventDefault(), return false, etc) if someone is trying to add two consecutive spaces like this:
document.getElementById("div").addEventListener("keydown", function(e) {
function stop() {
alert("two whitespaces detected");
e.preventDefault();
return false;
}
});
Third, add an if statement inside the listener to check if the key last pressed was space like this:
document.getElementById("div").addEventListener("keydown", function(e) {
function stop() {
alert("two whitespaces detected");
e.preventDefault();
return false;
}
if (e.code == "Space" || e.key == " ") {
}
});
Fourth, assign the value of whatever has been typed on the div to a variable, say content as a string and also assigned another variable, say prevVal to the last character of the content string like this:
document.getElementById("div").addEventListener("keydown", function(e) {
function stop() {
alert("two whitespaces detected");
e.preventDefault();
return false;
}
if (e.code == "Space" || e.key == " ") {
var divContent = e.target;
var content = divContent.innerText;
var prevVal = content.substr(content.length - 1);
}
});
Lastly, you can either use another if statement to check if the last key pressed is a white-space or not by comparing it to the assigned variable prevVal above and if it is, run the stop() function that we wrote earlier like this:
document.getElementById("div").addEventListener("keydown", function(e) {
function stop() {
alert("two whitespaces detected");
e.preventDefault();
return false;
}
if (e.code == "Space" || e.key == " ") {
var divContent = e.target;
var content = divContent.innerText;
var prevVal = content.substr(content.length - 1);
if (prevVal.trim() === '') {
stop();
} else {
console.log("nevermind");
}
}
});
Check the code snippet below or go to this jsFiddle to see the above code in action:
var div = document.getElementById("div");
div.addEventListener("keydown", function(e) {
function stop() {
alert("two whitespaces detected");
e.preventDefault();
return false;
}
if (e.code == "Space" || e.key == " ") {
var divContent = e.target;
var content = divContent.innerText;
var prevVal = content.substr(content.length - 1);
if (prevVal.trim() === '') {
stop();
} else {
console.log("nevermind");
}
}
});
#div {border: 1px solid #000;}
<div contenteditable="true" id="div"></div>
Update
This implementation ignores newline characters and will cause bugs if they are added by the user (In the app i'm implementing, I don't allow the user to add any newline characters, so this implementation works). For a full implementation please have a look at #WASD 's answer.
Update TLDR: Implementation works on Firefox, Edge, Safari, Chrome, and Opera when 'Enter' key is not allowed.
After some playing around, I finally figured out a solution. In firefox, .innerText is very buggy. Therefore, we need to use .textContent instead which works well for all browsers. If we use .textContent, in all browsers except Firefox, when the user presses the spacebar, a character that looks like a space is added to the content, but IT IS NOT a space. It's actually a different character which is the ASCII character 160. In Firefox, the normal ASCII character 32 is used for the space. Now that we have this information, we can get started on solving the problem. Here is a cross-browser implementation that works.
document.getElementById("div")
.addEventListener("keydown", function(e) {
if (e.key === " ") {
const text = this.textContent,
i = window.getSelection().anchorOffset;
const chars = text.substring(i - 1, i + 1);
if (chars.includes(" ") ||
chars.includes(String.fromCharCode(160))) {
e.preventDefault();
return false;
}
}
});
<div style="border: 2px solid black;" contenteditable=true id=div></div>
Firstly, we use .textContent instead of .innerText. Additionally, in the if statement, we check if the text in the contenteditable div includes the conventional space char (ASCII 32) and we also check if it includes the ASCII 160 which is used by every browser except FF. If the text includes the 160 or 32 char, then we need to preventDefault() and stop the user from adding multiple space characters consecutively.
Related
I'm coding a chat box. And the Characters that I enter, is not reflected as it is.
This is basically the code I'm using.
$(document).ready(function() {
$(".entry").keydown(function(event) {
console.log(String.fromCharCode(event.which));
});
});
And so when I type (lower-case) "a", console tab shows me "A".
special characters will not get reflected unless I create separate condition for it.
Could someone help me with a different function which does it all by itself, and returns a string as entered by the user. Or a different approach to this challenge all together. Thanks.
Actual code - chat.js
var str='';
$(document).ready(function() {
$(".entry").keydown(function(event) {
console.log(event.which);
if (event.which === 13 && event.shiftKey === false) {
console.log(str);
event.preventDefault();
} else {
var c = event.which;
str = str.concat(String.fromCharCode(c));
}
});
});
So basically the every character entered would get concated to the string. and Enter key would dump the text to console.
It's seems that trying to get the value of event.which in keydown event could lead you to a wrong ascii code (What you need to pass to String.fromCharCode).
See https://stackoverflow.com/a/10192144/3879872
I don't know if it fits your needs, but you could try:
$(document).ready(function(){
$(".entry").keypress(function(event) {
console.log(String.fromCharCode(event.which));
});
});
(Note the use of keypress instead of keydown)
EDIT: Added working Demo
var str = '';
$(document).ready(function() {
$(".entry").keypress(function(event) {
console.log(event.which);
if (event.which === 13 && event.shiftKey === false) {
console.log(str);
event.preventDefault();
} else {
var c = event.which;
str = str.concat(String.fromCharCode(event.which));
}
console.log('Formated Text', str);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea class="entry"></textarea>
I'm trying to make a jQuery method that would delete wanted chars from selected elements.
For example:
$("input").disallowChars(/\D/g);// should disallow input of all non-digit elements in input elements
This is how I thought to do it, but it doesn't seem to work:
$.fn.disallowChars = function(regexp){
this.keyup(function(){
var value = $(this).val();
value.replace(regexp, "");
$(this).val(value);
});
return this;
};
$("input").disallowChars(/\D/g);
I'm a total newbie at this, how can I make it work.
Thanks
You could use String.fromCharCode() and keypress event instead:
$.fn.disallowChars = function(regexp){
return this.keypress(function(e){
if(String.fromCharCode(e.which).match(regexp)) return false;
});
};
DEMO
BUT doesn't disable any characters to be paste in input using mouse or paste keyboard shortcut.
On modern browsers, you could use input event, or change keyup paste mouseup (ya mouseup, to handle dropped text too):
$.fn.disallowChars = function(regexp){
return this.on('input', function(){
this.value = this.value.replace(regexp, '');
});
};
BUT then once input value is replaced, text carret is put in end (or start depending browser behaviour) of string input.
DEMO
heres a handy routine I use to sanitize some input fields in a current project:
// REPLACE SELECTOR WITH YOUR ID(S) OR SELECTORS...
$('input').bind("change keyup", function() {
var val = $.trim($(this).val());
// READ UP ON REGEX TO UNDERSTAND WHATS GOING ON HERE... ADD CHARACTERS YOU WANT TO ELIMINATE...
var regex = /[":'/\+;<>&\\/\n]/g;
if (val.match(regex)) {
val = val.replace(regex, "");
$(this).val($.trim(val));
}
});
Heres another version I used recently:
$("#myField").on("keypress", function(event) {
// THIS ONLY ALLOWS A-Z, A-Z, 0-9 AND THE # SYMBOL... just change stuffToAllow to suit your needs
var stuffToAllow = /[A-Za-z0-9# ]/g;
var key = String.fromCharCode(event.which);
if (event.keyCode == 8 || event.keyCode == 37 || event.keyCode == 39 || stuffToAllow.test(key)) {
return true;
}
alert( key + ' character not allowed!');
return false;
});
$(document).ready(function() {
$('.expanding').on('keyup',function() {
var textarea = document.getElementsByTagName('textarea')[0];
var submitBtn = document.getElementById('submit-btn');
if(textarea.value == '') {
$('#submit-btn').attr('disabled', true);
} else {
$('#submit-btn').attr('disabled', false);
}
});
});
Here's my code so far. It works fine, but it's missing a feature. So by default, the value of the textarea is an empty string and therefore the button is disabled. However, any whitespace entered enables the button, which I don't want.
Do I use else if? Or an or in the if statement? How do I write this?
Thanks.
You could use trim():
if(textarea.value.trim() == '')
Using the .trim() or $.trim() functions below, strip out the leading & ending whitespaces and then check to see if textarea has a string length (which it won't if it was just a bunch of spaces).
$(document).ready(function() {
$('.expanding').on('keyup',function() {
// Using vanilla javascript
var textarea = document.getElementsByTagName('textarea')[0].trim();
// or with jQuery
var textarea = $.trim(document.getElementsByTagName('textarea')[0]);
var submitBtn = document.getElementById('submit-btn');
if( !textarea.length ) {
$('#submit-btn').attr('disabled', true);
} else {
$('#submit-btn').attr('disabled', false);
}
});
});
Just strip leading and trailing whitespace off the value:
if(textarea.value.replace(/^\s*(.*?)\s*$/, "$1") == '') {
I'm using the CKEDITOR.inline(element) feature of CKEditor (contenteditable=true) in a "WYSIWYG" content editor that I'm working on. The thing works great for all kinds of tags that I've tested with except on:
When I go to edit a <button>, I can edit the text inside of the tag, just fine, except for "space".
When I hit the spacebar, instead of getting a space character inserted in the text, the button attempts to be "pressed", since, I'm assuming the default functionality of the browser is to try and "press" a button that is focused.
So.. I tried to "highjack" the $(element).on('keydown') event and checked for keycode 32 to apply preventDefault to the event. That technically worked to do the "highjacking" (I can see the below console.log), but I still don't get a "space" in the content.
var _fixButtonSpaceKeydown = function(elem, removeBinding){
console.log("_fixButtonSpaceKeydown()");
if(removeBinding){
jQuery(elem).off('keydown');
} else {
jQuery(elem).on('keydown', function (e) {
if (e.keyCode == 32) {
console.log('Caught space!');
e.preventDefault();
}
});
}
}
Has anyone come across this w/ CKEditor before, and have they found a solution?
Below workaround won't stop onclick() on Chrome while focusing on a button and type space. This is a browser issue and it seems there's no way to totally prevent the event. Anyway, we can still add space(Make sure to insert HTML encoding) at the cursor position:
jQuery(contentEditableBtn).attr('onclick','event.preventDefault(); insertSpace();');
function insertSpace(){
var range, node;
if (window.getSelection && window.getSelection().getRangeAt) {
range = window.getSelection().getRangeAt(0);
node = range.createContextualFragment(" ");
range.insertNode(node);
window.getSelection().collapseToEnd();
window.getSelection().modify("move", "forward", "character");
} else if (document.selection && document.selection.createRange) {
document.selection.createRange().pasteHTML(" ");
document.selection.collapseToEnd();
document.selection.modify("move", "forward", "character");
};
}
Maybe you could manually insert a space after preventDefault(). I'm not sure if this would work, but it's worth a try:
var _fixButtonSpaceKeydown = function(elem, removeBinding){
console.log("_fixButtonSpaceKeydown()");
if(removeBinding){
jQuery(elem).off('keydown');
} else {
jQuery(elem).on('keydown', function (e) {
if (e.keyCode == 32) {
console.log('Caught space!');
e.preventDefault();
e.target.innerHTML += ' ';
}
});
}
}
Try this: I haven't tested it but I'm sure you can add space by getting the cursor position where a char is then add a space before the char and replace the paragraph.
Something like this:
var _fixButtonSpaceKeydown = function(elem, removeBinding){
console.log("_fixButtonSpaceKeydown()");
if(removeBinding){
jQuery(elem).off('keydown');
} else {
jQuery(elem).on('keydown', function (e) {
var p = "";
if (e.keyCode == 32) {
console.log('Caught space!');
e.preventDefault();
p = e.target.innerHTML;
// get position of cursor
var cursorPosition = window.getSelection().getRangeAt(0).startOffset;
p.replace(p.charAt(position), " " + p.charAt(position));
// replace the content after change
e.target.innerHTML = p;
}
});
}
}
I thought it would be a simple thing to hijack the space key when in a form input so that it would function like a hyphen. Generally jQuery makes stuff like this really simple.
The code I tried is this:
$("#StreamUrl").keydown(function (e) {
if (e.keyCode == 32) return 109;
});
But this has no effect whatsoever. I tried a more simple script:
$("#StreamUrl").keydown(function (e) {
//if (e.keyCode == 32) return 109;
alert(e.keyCode);
});
This script correctly alerts 32 on space press and 109 on hyphen press. Also, I have no JavaScript errors.
Why wouldn't if (e.keyCode == 32) return 109; work? When I replace that line with if (e.keyCode == 32) alert("space!!"); I get the alert correctly, so I know the if is returning true correctly.
What gives?
Edit - Solution
Thanks to #Nick for pointing out the copy-paste issue. I ended up with a little bit of a hybrid. Here's the code that I have gotten to work which is both smooth and handles Copy/Paste.
$("#StreamUrl").keydown(function (e) {
if (e.keyCode == 32) {
$(this).val($(this).val() + "-"); // append '-' to input
return false; // return false to prevent space from being added
}
}).change(function (e) {
$(this).val(function (i, v) { return v.replace(/ /g, "-"); });
});
The problem is that return 109 doesn't do what you want it to do. In an event handler, you return true or false depending on whether or not you want the browser to execute the default action. In keydown, you would return false to prevent the character from being inserted.
$("#StreamUrl").keydown(function (e) {
if (e.keyCode == 32) {
$(this).val($(this).val() + "-"); // append '-' to input
return false; // return false to prevent space from being added
}
});
jsfiddle example
You usually want the keyup event instead here, which fires after the space has been added, something like this is a bit easier:
$("#StreamUrl").bind("keyup change", function () {
$(this).val(function(i, v) { return v.replace(/ /g,"-"); });
});
Try it out here, what this does is allow the space to be added, but then instantly does a replace of spaces for hyphens by passing a function to .val(). For older versions of jQuery, it'd look like this:
$("#StreamUrl").bind("keyup change", function () {
$(this).val($(this).val().replace(/ /g,"-"));
});
This works even for people pasting content, an easy way to get around keydown validation.