How to recreate the same effect of highlighting with the reference attached? - javascript

I am working on a typing game and I am trying to imitate the same effect of highlighting of the characters to be typed like in https://www.ratatype.com/typing-test/test/. However, it was harder than what I imagined.
I created code which uses replace function to give style on the character. However, it only replaces the first instance of the character and the other instances, it won't do what it was supposed to do.
const originTextDiv = document.getElementById('origin-text'); //the div that was supposed to be highlighted.
function spellCheck() {
let textEntered = textArea.value;
//let textCharEntered = textEntered.split("");
let originTextMatch = originText.substring(0, textEntered.length);
// console.log(textEntered);
var key = event.keyCode || event.charCode;
originChar = originText.split("");
//console.log(originTextDiv);
char = originTextDiv.textContent;
//console.log(char);
console.log(originChar[index]);
//console.log(textCharEntered);
if (textEntered == originText) {
textWrapper.style.borderColor = 'orange';
$("#myModal").modal();
stop();
} else {
if (textEntered == originTextMatch) {
textWrapper.style.borderColor = 'green';
textArea.style.backgroundColor = 'white';
// if (!(key == 20 || key == 16 || key == 18 || key == 8 || key == 46 || key ==17 )){
originTextDiv.innerHTML = char.replace(originChar[index], '<span style="background-color:green;">' +originChar[index]+ '</span>'); //here is the code where it is highlighting
++index;
// }
// if (key == 8 || key == 46){
// --index;
// }
} else {
textWrapper.style.borderColor = 'red';
textArea.style.backgroundColor='f23a49';
originTextDiv.innerHTML = char.replace(originChar[index], '<span style="background-color:red;">' +originChar[index]+ '</span>');
if (!(key == 8 || key == 46)){
error++;
}
}
}
}
I expected to have it work as intended but I didn't knew replace only replaces the first instance. I tried to look at on replaceAt functions for javascript but when I tried it the replaceAt also replaces the span tag attached. Could someone give me some pointers on how to recreate the same effect as the reference above?

Related

Would there be a shorter version of this part of code in JS / CSS / HTML programming?

A question in JS / CSS / HTML programming!
Again, I'm not good at asking the question pinpoint, and sorry for all the confusion. I shall talk about my real intention for this part of code, and see what solutions can be made.
I'd like to invite the users to input characters, which will be thrown into part of the variable characters for another function create_random_string().
If that's the case, what solutions can be made to shorten the code for the part document.addEventListener() for the sake of efficiency? Thank you very much!
var characters = '';
function create_random_string(string){
var random_string = '';
for (var i, i = 0; i < string; i++) {
random_string += characters.charAt(Math.floor(Math.random() * characters.length));
}
return random_string;
}
document.addEventListener('keydown', function(event) {
if(event.key == "a") {
characters += "a";
}
else if(event.key == "b") {
characters += "b";
}
else if(event.key == "c") {
characters += "c";
}
...
else if(event.key == "x") {
characters += "x";
}
else if(event.key == "y") {
characters += "y";
}
else if(event.key == "z") {
characters += "z";
}
Why not check for a range of keys:
document.addEventListener('keydown', function(event) {
if (event.key >= 'a' && event.key <= 'z'){
console.log(event.key);
}
e is Event Object. .key is an Event property that gets the key that was tapped. When testing a key event make sure to click the testing area to gain focus first.
document.onkeydown = logKey;
function logKey(e) {
console.log(e.key);
}
One way to do this that's even shorter than the other answers:
document.onkeydown = (e) => console.log(e.key);
This uses an inline function to further reduce the length of the answer by zer00ne.
If you are more interested in form validation than just logging to the console, you can also do something along the lines of the following:
function alphaOnly(event) {
let key = event.keyCode;
return ((key >= 65 && key <= 90) || key == 8);
};

how to compute tic-tac-toe board and find out match_status in javascript

i have created a javascript for tic-tak-toe.the input is given to the html using $('td').text("X")and$('td').text("O") for 2player,also i declared an array of winning combination.Now my problem is how to validate(function match_status() the board and determine the winner/loser or the match is drawn
$(document).ready(function(){
var count=0;
$('td').on('click',function(event)
{
if(count%2!=0)
{
$(this).text("X");
$("p").toggle('fast', function(){
$("h2").text('player one');
});
}
else
{
$(this).text("O");
$("p").toggle('fast', function(){
$("h2").text('player two');
});
}
count++;
checkboard(this.id);
});
//td ids
var wins = [["#0","#1","#2"], ["#3","#4","#5"], ["#6","#7","#8"], ["#0","#4","#8"], ["#2","#4","#6"], ["#0","#3","#6"], ["#1","#4","#7"], ["#2","#5","#8"]];
function checkboard()
{
for (var i=0; i<wins.length; i++)
{
var w = wins[i];
var checkXwin = $(w[0].val()) == "X" && w[1].val() == "X" && w[2].val() == "X";
var checkOwin = w[0].val() == "O" && w[1].val() == "O" && w[2].val() == "O";
if (checkXwin)
{
alert("X Wins!");
}
}
Try the following in the checkBoard function.
Change $(w[0].val()) to $(w[0]).text(). The $(w[0]) will pick the element by id, and .text() will pick the text without any formatting - so X or O in your case. Do that for each check. And I do mean each check. w[1].val() == "X" is incorrect. w[1] is a string, not an object.
The difference is that $(w[0].val()) is not an element, but $(w[0]) is, and then you can get the text from this element.

HTML5 restricting input characters

Is it possible to restrict the input of certain characters in HTML5/JavaScript? For example, could I have an input textbox on the screen and if the user tries to type a letter in it, it wouldn't show up in the box because I've restricted it to only numbers?
I know you can use a pattern which will be checked on submit, but I want the "bad" characters to just never be entered at all.
The input textbox
<input type="text" onKeyDown="myFunction()" value="" />
JavaScript
function myFunction() {
var e = event || window.event; // get event object
var key = e.keyCode || e.which; // get key cross-browser
if (key < 48 || key > 57) { //if it is not a number ascii code
//Prevent default action, which is inserting character
if (e.preventDefault) e.preventDefault(); //normal browsers
e.returnValue = false; //IE
}
}
Use html5 pattern attribute for inputs:
<input type="text" pattern="\d*" title="Only digits" />
OR
Use html5 number type for input :
<input type="number" />
To slightly improve off of jonhopkins excellent answer, I added backspace and delete key acceptance like so:
function inputValidate(){
var e = event || window.event;
var key = e.keyCode || e.which;
if (((key>=48)&&(key<=57))||(key==8)||(key == 46)) { //allow backspace //and delete
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
}
}
For Restricting Characters symbols like '-' and ','
<input type="text" pattern="[^-,]+">
for restricting numbers
<input type="text" pattern="[^0-9]+">
for restricting letters of the alphabet
<input type="text" pattern="[^a-zA-Z]+">
KeyboardEvent.keyCode is deprecated, so here's a solution using the HMLElement.input event. This solution uses a simple regex, and handles copy-paste nicely as well by just removing the offending elements from any input.
My regex: /[^\w\d]/gi
Matches anything not (^) a word character (\w: a-z) or a digit (\d: 0-9).
g modifier makes regex global (don't return after first match)
i modifier makes regex case insensitive
With this regex, special characters and spaces won't be allowed. If you wanted to add more, you'd just have to add allowed characters to the regex list.
function filterField(e) {
let t = e.target;
let badValues = /[^\w\d]/gi;
t.value = t.value.replace(badValues, '');
}
let inputElement = document.getElementById('myInput');
inputElement.addEventListener('input', filterField);
<input id="myInput" type="text" style="width: 90%; padding: .5rem;" placeholder="Type or paste (almost) anything...">
//improved wbt11a function
function numberFieldStrictInput(allowcomma, allownegative) {
var e = event || window.event; // get event object
var key = e.keyCode ||`enter code here` e.which; // get key cross-browser
if(key==8 || key==46 || key == 9 || key==17 || key==91 || key==18 ||
key==116 || key==89 || key==67 || key==88 || key==35 || key==36) //back, delete tab, ctrl, win, alt, f5, paste, copy, cut, home, end
return true;
if(key == 109 && allownegative)
return true;
if(key == 190 && allowcomma)
return true;
if(key>=37 && key<=40) //arrows
return true;
if(key>=48 && key<=57) // top key
return true;
if(key>=96 && key<=105) //num key
return true;
console.log('Not allowed key pressed '+key);
if (e.preventDefault) e.preventDefault(); //normal browsers
e.returnValue = false; //IE
}
//on input put onKeyDown="numberFieldStrictInput(1,0)"
What about this (it supports special keys, like copy, paste, F5 automatically)?
function filterNumericInput() {
var e = event || window.event; // get event object
if (e.defaultPrevented) {
return;
}
const key = e.key || e.code;
if ((e.key.length <= 1) && (!(e.metaKey || e.ctrlKey || e.altKey))) {
if (!((key >= '0' && key <= '9') || (key === '.') || (key === ',') || (key === '-') || (key === ' '))) {
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
}
}
Limit input to letters, numbers and '.' (for React users only)
Here is my simple solution, I couldn't find a better solution for React and made my own. 3 steps.
First, create a state.
const [tagInputVal, setTagInputVal] = useState("");
Then, use the state as input value (value={tagInputVal}) and pass the event to the onChange handler.
<input id="tag-input" type="text" placeholder="Add a tag" value={tagInputVal} onChange={(e) => onChangeTagInput(e)}></input>
Then, set the value of the event inside onChange handler.
function onChangeTagInput(e) {
setTagInputVal(e.target.value.replace(/[^a-zA-Z\d.]/ig, ""));
}
var keybNumberAndAlpha = new keybEdit(' 0123456789abcdefghijklmnopqrstuvwxyz');
function keybEdit(strValid, strMsg) {
var reWork = new RegExp('[a-z]','gi'); // Regular expression\
// Properties
if(reWork.test(strValid))
this.valid = strValid.toLowerCase() + strValid.toUpperCase();
else
this.valid = strValid;
if((strMsg == null) || (typeof(strMsg) == 'undefined'))
this.message = '';
else
this.message = strMsg;
// Methods
this.getValid = keybEditGetValid;
this.getMessage = keybEditGetMessage;
function keybEditGetValid() {
return this.valid.toString();
}
function keybEditGetMessage() {
return this.message;
}
}
function editKeyBoard(ev, objForm, objKeyb) {
strWork = objKeyb.getValid();
strMsg = ''; // Error message
blnValidChar = false; // Valid character flag
var BACKSPACE = 8;
var DELETE = 46;
var TAB = 9;
var LEFT = 37 ;
var UP = 38 ;
var RIGHT = 39 ;
var DOWN = 40 ;
var END = 35 ;
var HOME = 35 ;
// Checking backspace and delete
if(ev.keyCode == BACKSPACE || ev.keyCode == DELETE || ev.keyCode == TAB
|| ev.keyCode == LEFT || ev.keyCode == UP || ev.keyCode == RIGHT || ev.keyCode == DOWN) {
blnValidChar = true;
}
if(!blnValidChar) // Part 1: Validate input
for(i=0;i < strWork.length;i++)
if(ev.which == strWork.charCodeAt(i) ) {
blnValidChar = true;
break;
}
// Part 2: Build error message
if(!blnValidChar)
{
//if(objKeyb.getMessage().toString().length != 0)
// alert('Error: ' + objKeyb.getMessage());
ev.returnValue = false; // Clear invalid character
ev.preventDefault();
objForm.focus(); // Set focus
}
}
<input type="text"name="worklistFrmDateFltr" onkeypress="editKeyBoard(event, this, keybNumberAndAlpha)" value="">
I found that onKeyDown captures Shift key, arrows, etc. To avoid having to account for this, I could filter out character input easily by subscribing to onKeyPress instead.
Since many of the answers above didn't satisfy me, I propose my solution which solves the problem of the input event being uncancelable by storing the previous value in a custom attribute, and restoring it in case the pattern is not matched:
const input = document.querySelector('#input-with-pattern')
input.addEventListener('keyup', event => {
const value = event.target.value;
if (!/^[a-zA-Z]+$/.test(value) && value !== '') { // it will allow only alphabetic
event.target.value = event.target.getAttribute('data-value');
} else {
event.target.setAttribute('data-value', value);
}
});
<input id="input-with-pattern">

Jquery keydown() with number format not work correctly on android webview

I have encountered a strange behavior on android browser / webview. I was testing an input that will automatically format to phone number format "(xxx) xxx-xxxx". But then what happened was when I tapped or press any number on androids keyboard, the first input was like this "(x" but then the cursor was in between "(" and "x". Is there a way to put the cursor after "x" value?
I tested this on iPhone and windows web browsers and it works fine. Please let me know if there are mistakes on my jquery or javascripts.
Thanks
HTML CODE:
<html>
<head>
<title>Sample Phone Number Format</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
</head>
<body>
<input type="text" id="phone" />
<script type="text/javascript">
$('#phone').on('keydown', function (event) {
objval = $(this).val();
if (event.keyCode == 46 || event.keyCode == 8) {} else {
if (!((event.keyCode > 47 && event.keyCode < 58) || (event.keyCode > 95 && event.keyCode < 106) || (objval.length > 13))) {
event.preventDefault();
} else {
if (objval.length == 0) {
$(this).val(objval + '(');
alert(objval + '(');
} else if (objval.length == 4) {
$(this).val(objval + ') ');
alert(objval + ') ');
} else if (objval.length == 9) {
$(this).val(objval + '-');
alert(objval + '-');
} else if (objval.length >= 14) {
if (event.keyCode == 9) {
return;
} else {
event.preventDefault();
}
}
}
}
});
$('#phone').on('keydown', function (event) {
var objVal = $(this).val();
if(objVal.length < 14)
{
validateCallerForm(objVal + String.fromCharCode((96 <= event.keyCode && event.keyCode <= 105)? event.keyCode-48 : event.keyCode));
}
});
//Validates proper phone format, true if valid phone number, false otherwise
function isValidPhoneNumber(elementValue) {
var numberPattern = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
return numberPattern.test(elementValue);
}
//validates entire caller form, also updates css classes for proper response
function validateCallerForm(PhoneNumber) {
if (isValidPhoneNumber(PhoneNumber)) {
alert("true");
} else {
alert("false");
}
}
</script>
</body>
</html>
Giving +50 Bounty to who'm will answer this correctly
you have to first define listener for typing and copy-paste like below (not required) :
$("#phone").keyup( function() {
maskLine(this);
});
$("#phone").change( function() {
maskLine(this);
});
Then, to manage cursor placement, you have to cache previous phone number and then, you could compare difference and update cursor position if needed.
So declare, you have to declare a global array like this :
var _cacheElementValues = new Array();
At last, you can check the function below, it applies your mask to phone number field and manage cursor placement :
function maskLine( element ) {
element = $(element);
var maskedLine = '';
var line = element.attr('value');
// check the cache of the input and abord if no change since last treatment
if (_cacheElementValues[element.attr('id')] != undefined && _cacheElementValues[element.attr('id')] == line) {
return;
}
line = line.replace(/\D/g, ''); // remove all characters != digits
line = line.substring(0, 10);
if (line != '') {
// apply mask
if (line.length <= 2 ) {
maskedLine = "(" + line;
} else if (line.length < 6) {
maskedLine = line.replace(/^([0-9]{3})([0-9]{0,3})/g, '($1) $2');
} else {
// mask : '(XXX) XXX-XXXX'
maskedLine = line.replace(/^([0-9]{3})([0-9]{3})([0-9]{0,4})/g, '($1) $2-$3');
}
}
// define cursor position at the end of the input by default
var pos = maskedLine.length;
// Change cursor placement if necessary
if (typeof element[0].selectionStart != 'undefined') {
var start = element[0].selectionStart;
var end = element[0].selectionEnd;
var insText = element[0].value.substring(start, end);
// get current cursor placement
if (insText.length == 0) {
pos = start;
} else {
pos = start + insText.length;
}
// find how many digits typing since last mask application
var previousLength = 0;
if (_cacheElementValues[element.attr('id')] != undefined) {
previousLength = _cacheElementValues[element.attr('id')].replace(/\s/g, '').length;
}
var diff = maskedLine.replace(/\s/g, '').length - previousLength;
// if sum of new typing digit is > 0 : we change cursor placement
if (diff > 0) {
pos += (diff - 1) + Math.round((diff-1)/3);
if (pos%6 == 0 && maskedLine.length >= pos+1) pos++;
}
}
// update input data & cache
element.val(maskedLine);
_cacheElementValues[element.attr('id')] = maskedLine;
// update cursor placement
element[0].selectionStart = element[0].selectionEnd = pos;
}
You can find this example on jsFiddle : http://jsfiddle.net/UE9LB/5/
I hope this little explantion can solve your problem ;)
Enjoy !
ps: i apologize for my poor english :s
I'd recommend at least starting with an existing plugin rather than going through your own isolated rounds of solving issues.
http://digitalbush.com/projects/masked-input-plugin/
https://github.com/igorescobar/jQuery-Mask-Plugin
The short answer is to set the selectionStart and selectionEnd properties of the input element. After you format the value, set these properties to this.value.length.
this.selectionStart = this.value.length;
this.selectionEnd = this.value.length;
But, where you are going to run into trouble is when the cursor is not at the end of the text. Eg, the user has manually positioned the cursor to a position within the text. To prevent the cursor from jumping to the end, you will need to detect the cursor position before you format the input, then put the cursor back in the appropriate position after formatting.
Edit: This jsFiddle may get you started, but isn't perfect yet.
I rewrite the code on my #phone keydown event and this will work on iPhone, Android, webkit browsers.
$('#phone').on('keydown', function (event) {
if (event.keyCode == 8 || event.keyCode == 37 || event.keyCode == 39) {
// ignore if BKSPCE, left arrow, or right arrow
} else {
// validate if anything else
inputval = $(this).val();
var string = inputval.replace(/[^0-9]/g, "");
var first3 = string.substring(0,3);
var next3 = string.substring(3,6);
var next4 = string.substring(6,10);
var string = ("(" + first3 + ") " + next3 + "-" + next4);
$(this).val(string);
}
});

Autocomplete script getting Object expected error

At the url http://www.candyundies.com/template_non_product.php, I am using an autocomplete script on the search box for suggestions. I have tested and is working in current versions of Chrome, Safari, Opera, Firefox and IE 8. However, I noticed in IE 8, it is throwing an Object expected error after the first letter is typed in the search box but the script continues to work flawlessly. I'm sure it is a syntax error or something small I have overlooked but I cannot seem to find the problem. Any help would be much appreciated.
Contents of autocomplete.js:
// global variables
var acListTotal = 0;
var acListCurrent = -1;
var acDelay = 100;
var acURL = null;
var acSearchId = null;
var acResultsId = null;
var acSearchField = null;
var acResultsDiv = null;
function setAutoComplete(field_id, results_id, get_url) {
// initialize vars
acSearchId = "#" + field_id;
acResultsId = "#" + results_id;
acURL = get_url;
// create the results div
$("#auto").append('<div id="' + results_id + '"></div>');
// register mostly used vars
acSearchField = $(acSearchId);
acResultsDiv = $(acResultsId);
// on blur listener
acSearchField.blur(function(){ setTimeout("clearAutoComplete()", 100) });
// on key up listener
acSearchField.keyup(function (e) {
// get keyCode (window.event is for IE)
var keyCode = e.keyCode || window.event.keyCode;
var lastVal = acSearchField.val();
// check an treat up and down arrows
if(updownArrow(keyCode)){
return;
}
// check for an ENTER or ESC
if(keyCode == 13 || keyCode == 27){
clearAutoComplete();
return;
}
// if is text, call with delay
setTimeout(function () {autoComplete(lastVal)}, acDelay);
});
}
// treat the auto-complete action (delayed function)
function autoComplete(lastValue) {
// get the field value
var part = acSearchField.val();
// if it's empty clear the resuts box and return
if(part == ''){
clearAutoComplete();
return;
}
// if it's equal the value from the time of the call, allow
if(lastValue != part){
return;
}
// get remote data as JSON
$.getJSON(acURL + part, function(json){
// get the total of results
var ansLength = acListTotal = json.length;
// if there are results populate the results div
if(ansLength > 0){
var newData = '';
// create a div for each result
for(i=0; i < ansLength; i++) {
newData += '<div class="unselected">' + json[i] + '</div>';
}
// update the results div
acResultsDiv.html(newData);
acResultsDiv.css("display","block");
// for all divs in results
var divs = $(acResultsId + " > div");
// on mouse over clean previous selected and set a new one
divs.mouseover( function() {
divs.each(function(){ this.className = "unselected"; });
this.className = "selected";
});
// on click copy the result text to the search field and hide
divs.click( function() {
acSearchField.val(this.childNodes[0].nodeValue);
clearAutoComplete();
});
} else {
clearAutoComplete();
}
});
}
// clear auto complete box
function clearAutoComplete() {
acResultsDiv.html('');
acResultsDiv.css("display","none");
}
// treat up and down key strokes defining the next selected element
function updownArrow(keyCode) {
if(keyCode == 40 || keyCode == 38){
if(keyCode == 38){ // keyUp
if(acListCurrent == 0 || acListCurrent == -1){
acListCurrent = acListTotal-1;
}else{
acListCurrent--;
}
} else { // keyDown
if(acListCurrent == acListTotal-1){
acListCurrent = 0;
}else {
acListCurrent++;
}
}
// loop through each result div applying the correct style
acResultsDiv.children().each(function(i){
if(i == acListCurrent){
acSearchField.val(this.childNodes[0].nodeValue);
this.className = "selected";
} else {
this.className = "unselected";
}
});
return true;
} else {
// reset
acListCurrent = -1;
return false;
}
}
Issue resolved. See comment by ocanal.

Categories

Resources