I have a problem with regex.
In my code I have an input when a player can write his name and then click the 'start' button to start the adventure.
The first condition is working fine. The player gets an error when the name isn't between 4 and 20 characters long.
When it comes to the next condition. The player gets an error no matter what will he write inside the input. When I changed the match() to test() there is an error in console, that the test() is not a function.
Please help me, what did I do wrong?
let actualPlace = 'mainMenu';
let playerName = '';
const playerNameContainer = document.querySelector('.playerName');
const start = document.querySelector('.start');
const regExpression = /^[a-zA-ZżźćńółęąśŻŹĆĄŚĘŁÓŃ0-9]$/;
start.addEventListener('click', function(){
if (actualPlace == 'mainMenu') {
playerNameValue = playerNameContainer.value;
playerNameLength = playerNameValue.length;
if (playerNameLength >= 4 && playerNameLength <= 20) {
if (playerNameValue.match(regExpression)) {
actualPlace = 'adventure';
playerName = playerNameContainer.value;
playerNameContainer.value = '';
} else {
errorMsg.innerHTML = 'error';
}
} else {
errorMsg.innerHTML = 'error';
}
}
})
your regular expression is matching only one single character; you need to add the + quantifier to match more than one character.
I also added different errors so you know what part is failing
Here its a working example:
let actualPlace = 'mainMenu';
let playerName = '';
const playerNameContainer = document.querySelector('.playerName');
const start = document.querySelector('.start');
const regExpression = /^[a-zA-ZżźćńółęąśŻŹĆĄŚĘŁÓŃ0-9]+$/;
const errorMsg = document.querySelector('.error');
start.addEventListener('click', function(){
if (actualPlace == 'mainMenu') {
playerNameValue = playerNameContainer.value;
playerNameLength = playerNameValue.length;
if (playerNameLength >= 4 && playerNameLength <= 20) {
if (playerNameValue.match(regExpression)) {
actualPlace = 'adventure';
playerName = playerNameContainer.value;
playerNameContainer.value = '';
errorMsg.innerHTML = 'Success!';
} else {
errorMsg.innerHTML = 'error: your name must not contain spetial characters';
}
} else {
errorMsg.innerHTML = 'error: your name must be between 4 and 20 characters';
}
}
})
<input class="playerName" /><br />
<button class="start" >Start</button>
<div class="error" ></div>
Related
Here's the code i've written, where when i write a word into e search field, it appends it in the element "word" by displaying it letter by letter. But, the problem is, that i don't know how to write the code that when i write another word in the search field, it deletes the word that appear to element "Word", then writes the new one i've written.
let text = document.getElementById("txt");
let elem = document.getElementsByClassName("target")[0];
let word = elem.querySelector(".word");
let btn = document.getElementsByClassName("btn")[0];
let error = document.querySelector('#error');
i = 0;
word.style.color = "#ffe100";
btn.addEventListener("click", function init() {
if (text.value == "") {
error.style.opacity = '1.0';
} else {
error.style.opacity = '0.0';
let save = word.textContent += text.value.charAt(i);
i++;
}
if (i < text.value.length) {
window.setTimeout(init, 100);
}
});
I've try many of alternatives, but there's no result.
I will iteratively change and/or improve your code in this answer, and will try to comment on each change in the code.
Refactor
First off, I'll have an easier time explaining the different approaches after refactoring your code:
// Prefer `const` over `let` when applicable
const input = document.querySelector("input"); // Previously: text
const output = document.querySelector("output"); // Previously: word
const button = document.querySelector("button"); // Previously: btn
const error = document.getElementById("error");
// You forgot `let`. Always declare your variables!
let i = 0;
button.addEventListener("click", () => {
if (input.value == "") {
error.style.opacity = '1.0';
} else {
error.style.opacity = '0.0';
reveal();
}
});
// Moved code from listener to here
function reveal() {
// Removed `save`; was unused
output.textContent += input.value[i];
++i;
// Moved here because it only runs if `input.value !== ""` anyways
if (i < input.value.length) {
setTimeout(reveal, 100);
}
}
/* Prefer to use CSS for static styles! */
.word {
color: #ffe100;
}
<!-- I assume your HTML to have looked similar to this: -->
<input><button>Submit</button>
<div>
<output></output>
</div>
<p id="error">Please submit (actual) text.</p>
Now let's take a look at your refactored code from above:
There is no resetting: Revealing can only continue (text can only be added).
The value of input is referenced directly: When its value changes...
Then revealing may stop prematurely.
Then the further revealed text may not represent the entered text upon button-press.
Allow reusability
The issue of point 1 can be solved by (re-)setting i and output.textContent in the listener. To solve point 2, we need to use some buffer for the text:
const input = document.querySelector("input");
const output = document.querySelector("output");
const button = document.querySelector("button");
const error = document.getElementById("error");
let i = 0;
let text = ""; // The buffer
button.addEventListener("click", () => {
if (input.value == "") {
error.style.opacity = '1.0';
} else {
error.style.opacity = '0.0';
// (Re-)setting
i = 0;
text = input.value;
output.textContent = "";
reveal();
}
});
function reveal() {
output.textContent += text[i];
++i;
if (i < text.length) {
setTimeout(reveal, 100);
}
}
.word {
color: #ffe100;
}
<input><button>Submit</button>
<div>
<output></output>
</div>
<p id="error">Please submit (actual) text.</p>
With these two small changes, your code now successfully deletes the revealed text in place for new text!
Adding states
But the deletion doesn't happen letter-by-letter. That would require some way to keep track of whether we are deleting or revealing.
Let's use a state-machine that –upon prompting– deletes the already revealed text (if any) and then reveals the new text:
const input = document.querySelector("input");
const output = document.querySelector("output");
const button = document.querySelector("button");
const error = document.getElementById("error");
let i = 0;
let text = "";
let state = "nothing"; // The state
button.addEventListener("click", () => {
if (input.value == "") {
error.style.opacity = '1.0';
} else {
error.style.opacity = '0.0';
// Remember previous state (for below)
const previousState = state;
// Issue deletion and update text-to-reveal
state = "delete";
text = input.value;
if (previousState === "nothing") {
// Start state-machine
nextCharacter();
}
}
});
// Rename function
function nextCharacter() {
if (state === "nothing") return;
if (state === "delete") {
output.textContent = output.textContent.slice(0, i);
// Finished deleting?
if (i === 0) {
const requiresRevealing = i < text.length;
state = requiresRevealing ? "reveal" : "nothing";
} else {
--i;
}
} else if (state === "reveal") {
output.textContent += text[i];
++i
// Finished revealing?
if (i === text.length) {
state = "nothing";
}
}
// Requires continuing?
if (state !== "nothing") {
setTimeout(nextCharacter, 100);
}
}
.word {
color: #ffe100;
}
<input><button>Submit</button>
<div>
<output></output>
</div>
<p id="error">Please submit (actual) text.</p>
Code quality refactoring
The code now works, but(!) the logic is scattered everywhere, and you need to know what variables to update for the revealing to work correctly. Instead, we could make use of classes:
const input = document.querySelector("input");
const output = document.querySelector("output");
const button = document.querySelector("button");
const error = document.getElementById("error");
// Move related variables and functions into class
class Revealer {
output;
index = 0;
text = "";
state = "nothing";
constructor(output) {
this.output = output;
}
// Moved from listener
reveal(text) {
const previousState = this.state;
this.state = "delete";
this.text = text;
if (previousState === "nothing") {
this.next();
}
}
// Previously nextCharacter()
next() {
if (this.state === "nothing") return;
if (this.state === "delete") {
this.deleteCharacter();
} else if (this.state === "reveal") {
this.revealCharacter();
}
if (this.state !== "nothing") {
setTimeout(() => this.next(), 100);
}
}
// Use more specific functions for readability
deleteCharacter() {
this.output.textContent = this.output.textContent.slice(0, this.index);
if (this.index === 0) {
const requiresRevealing = this.index < this.text.length;
this.state = requiresRevealing ? "reveal" : "nothing";
} else {
--this.index;
}
}
revealCharacter() {
this.output.textContent += this.text[this.index];
++this.index;
if (this.index === this.text.length) {
this.state = "nothing";
}
}
}
const revealer = new Revealer(output);
button.addEventListener("click", () => {
if (input.value == "") {
error.style.opacity = '1.0';
} else {
error.style.opacity = '0.0';
// Use simple call
revealer.reveal(input.value);
}
});
.word {
color: #ffe100;
}
<input><button>Submit</button>
<div>
<output></output>
</div>
<p id="error">Please submit (actual) text.</p>
Ideas for practicing
While the above code works, there are still ways to improve it or implement it differently:
Only delete until the remaining text is the same as the leading substring of the text-to-add.
You can use promises (with async/await) instead of using setTimeout() directly.
You can implement the revealing as functionality of a custom element.
...
Try implementing (one of) these as practice!
I use OOP system. I have one product list add project and ı want to capitalization check. I know two solution for problem but doesn't work.
For example, I entered a product called "bag" and again I want the same product not to be capitalized. Different letters such as "bag" and "Bag" or "BAG" should not be written in capital letters. how can ı fix this? Thanks advance for answer. (You just need to answer one of the two solutions.) Full code: https://codepen.io/BerkayAkgurgen/pen/bGBNojw
// Some Solution
function addProductToUI(e) {
const productName = nameInput.value.trim().toLowerCase();
const productModel = modelInput.value.trim();
const productPrice = priceInput.value.trim();
let products = getTodosFromStorage();
const urunlerim = new Urunler(productName, productModel, productPrice);
if (productModel == "" || productName == "" || productPrice == "") {
UI.showAlert("danger", "Hatalı Giriş")
} else if (todos.some(a => a.trim().toLowerCase() == newTodoValuee)) {
console.log("sdaas");
return false;
} else {
let control = false;
const products = Storage.getProductsFromStorage();
products.forEach(function (product) {
if (productName === product.productName) {
control = true;
}
});
if (control === false) {
UI.urunEkle(urunlerim);
Storage.addProductsToStorage(urunlerim);
UI.showAlert("success", "Başarılı Giriş");
nameInput.value = "";
modelInput.value = "";
priceInput.value = "";
} else {
UI.showAlert("danger", "Aynı Marka Girilemez");
}
}
e.preventDefault();
}
// Map Solution
function addProductToUI(e) {
const productName = nameInput.value.trim().toLowerCase();
const productModel = modelInput.value.trim();
const productPrice = priceInput.value.trim();
let products = getTodosFromStorage();
var words = todos.map(w => w.toLowerCase());
const urunlerim = new Urunler(productName, productModel, productPrice);
if (productModel == "" || productName == "" || productPrice == "") {
UI.showAlert("danger", "Hatalı Giriş")
} else if (words.includes(productName)) {
e.preventDefault();
console.log("Aynı Todo");
nameInput.value = "";
return false;
} else {
let control = false;
const products = Storage.getProductsFromStorage();
products.forEach(function (product) {
if (productName === product.productName) {
control = true;
}
});
if (control === false) {
UI.urunEkle(urunlerim);
Storage.addProductsToStorage(urunlerim);
UI.showAlert("success", "Başarılı Giriş");
nameInput.value = "";
modelInput.value = "";
priceInput.value = "";
} else {
UI.showAlert("danger", "Aynı Marka Girilemez");
}
}
e.preventDefault();
}
Ok, your code is huge, makes sense I guess, so I'll just give you a function you can use to only return common letters from a given text element(so that you can work with it)
The idea of how the function is used is that you store all values as lowercase FROM THE INPUT and do checks when INPUT IS ALREADY LOWERED
var x=document.getElementById('x')
function lowerCaseInput(elem){return elem.value.toLowerCase()}
var obj={}
document.getElementById('b')
.addEventListener('click',function(ev){
var input=lowerCaseInput(x)
if(obj[input]){alert(`Error: "${input}" already exists`)}
else{obj[input]=1; console.log(obj)}
})
<input id="x" />
<button id="b">add value</button>
At the moment I have an if the condition that checks if any of the string variable lengths is greater than 2 if true check for another condition, else console.log the output.
var previous_data_change = 'last_changed on 10/01/2019 13:56:34';
var current_data_change= "";
var current_data_end = "";
var current_data_profile = "normal";
// check for changes
if (
previous_data_change.length >= 2 ||
current_data_start.length >= 2 ||
current_data_end.length >= 2 ||
current_data_profile.length >= 2
) {
if (previous_data_change.includes("last_changed")) {
console.log(`last change comments: ${previous_data_change}`)
}
} else {
console.log(`no change in previous record`)
}
i have tried refactoring it using some,
var previous_data_change = 'last_changed on 10/01/2019 13:56:34';
var current_data_change= "";
var current_data_end = "";
var current_data_profile = "normal";
var filter_col = [
previous_data_change,
current_data_change,
current_data_end,
current_data_profile
];
change_boolean = filter_col.some((element) => element.length >= 2);
if (change_boolean && previous_data_change.includes("last_changed")) {
console.log(`last change comments: ${previous_data_change}`);
} else {
console.log("no change in previous record");
}
is there any way to shorten it further?
Since you want any of them to be length greater than 2. You can simply merge them instead of writing 4 if conditions.
var previous_data_change = 'last_changed on 10/01/2019 13:56:34';
var current_data_change= "";
var current_data_end = "";
var current_data_profile = "normal";
var string_to_check = previous_data_change + current_data_start + current_data_end + current_data_profile;
// check for changes
if (string_to_check.length < 2) {
console.log(`no change in previous record`)
return false;
}
if (previous_data_change.includes("last_changed")) {
console.log(`last change comments: ${previous_data_change}`)
return true;
}
I want to get the text before the character '/' from the string. It is repeating itself many times in my string and hence not able to get its index. So, how do I get the text before it? For example string "C:/users/username/desktop/lib" and if I click on the slash before username it should show "C:/users".
I have already tried indexOf() and CharAt() functions but in vain as they aren't appropriate to the problem.
Please try to provide the answer in JavaScript and not JQuery.
I think you want to do something like this...
let el = document.querySelector('p'),
text = el['innerText'],
characters = text.split('');
el.innerHTML = '';
characters.forEach(char => {
let span = document.createElement('span');
span.innerText = char;
span.addEventListener('click', onClick);
el.appendChild(span);
});
function onClick() {
let position = 0,
el = this;
while (el.previousSibling !== null) {
position++;
el = el.previousSibling;
}
let newText = text.substring(0, position);
console.log(newText.substring(0, newText.lastIndexOf('/')));
}
<p>C:/users/username/desktop/lib</p>
I tried do something like this
<div class="display">
</div>
const display = document.querySelector(".display");
const begin = "C:/users/username/desktop/lib"
display.textContent= begin
display.addEventListener("click", getSelectionPosition, false);
let bool = false
function getSelectionPosition () {
if(bool == false){
var selection = window.getSelection();
const index = selection.focusOffset;
console.log(display.textContent[index]);
if(display.textContent[index] == "/"){
display.textContent = display.textContent.substring(0, index);
}
else if(display.textContent[index-1] == "/"){
display.textContent = display.textContent.substring(0, index-1);
}
else if(display.textContent[index+1] == "/"){
display.textContent = display.textContent.substring(0, index+1);
}
}
else{
display.textContent = begin;
}
bool = !bool;
}
I don't understand why the "alert" from the if/else conditioning always gets me an error - while its defenetly not!
I tried to change it to "switch" , get the variables inside and out the function , but none of this helped.
const randomNumb = {
x: Math.round((Math.random()) * 10 + 1),
y: Math.round((Math.random()) * 10 + 1)
};
const action1 = document.querySelector("#theAction");
const firstNum = document.querySelectorAll("#numbers")[0];
const secondNum = document.querySelectorAll("#numbers")[1];
firstNum.innerHTML = randomNumb.x;
secondNum.innerHTML = randomNumb.y;
if (firstNum > secondNum) {
action1.innerHTML = "-";
} else {
action1.innerHTML = "+";
}
const answer1 = randomNumb.x - randomNumb.y;
const answer2 = randomNumb.x + randomNumb.y;
const checkButton = document.querySelector("input[type=button][value=Check]");
const checkAnswer = document.querySelector("input[type=text]");
const theValue = checkAnswer.value;
checkButton.onclick = function() {
if (theValue === answer1 || theValue === answer2){
alert("Correct!")
} else {
alert("Error")
}
location.reload();
};
I'm trying to do that when I'm putting the correct answer in the "checkAnswer" it will put correct. if not , Error.
but it keeps giving me "Error" all the time.
Any suggestions ? (maybe I could write the code better in general?)
To verify to yourself check this link and check what type of an object you are trying to compare in your if/else statement.
const action1 = document.querySelector("#theAction");
const firstNum = document.querySelectorAll("#numbers")[0]; // this is a HTML element
const secondNum = document.querySelectorAll("#numbers")[1];
firstNum.innerHTML = randomNumb.x;
secondNum.innerHTML = randomNumb.y;
if (firstNum > secondNum) { // comparision is not possible
action1.innerHTML = "-";
} else {
action1.innerHTML = "+";
}
Let's access the value inside the HTML element and cast it to Number() for example
if (Number(firstNum.innerHTML) > Number(secondNum)) { // comparision is possible
action1.innerHTML = "-";
} else {
action1.innerHTML = "+";
}
this will solve the issue you are currently having, cheers!
I solved it!
checkButton.onclick = function() {
const theValue = checkAnswer.value;
if (theValue === answer1){
alert("correct")
} else if (theValue == answer2){
alert("correct")
} else {
alert("error")
};
};