JavaScript: if let / if-scoped let - javascript

I cannot seem to find anything similar to if let from other programming languages in JavaScript.
If I want to get the logo text of Stack Overflow I need to do
let text = document.querySelector('[class="-img _glyph"]')
if(text) {
result = text.innerText
//do some other work
}
So after declaring, I have to check for it to not be undefined first before using it. Now what would be much more logical is the following:
if let text = document.querySelector('[class="-img _glyph"]') {
result = text.innerText
//do some other work
}
which however doesn't work in JavaScript. Is there another syntax that I can use to avoid having to use the extra line just for the undefined-check?
I found this 10 year old thread https://esdiscuss.org/topic/if-scoped-let but since there were no further responses, I don't know if there is already anything that solves this.

Well, then the answer could be to use a for loop:
for (let text = document.querySelector('[class="-img _glyph"]'); text; text = false) {
result = text.innerText;
console.log(result);
}
console.log("done");
Alternatively - and more in line with a maintainable code - you could do
{
let text = document.querySelector('[class="-img _glyph"]');
if (text) {
result = text.innerText;
console.log(result);
}
console.log("text:", text);
}
console.log(text) // will throw an error!

You can't declare a variable within the if, but you can do an assignment and check that it's not undefined pretty easily:
let text, result;
if (text = document.querySelector('[class="-img _glyph"]')) {
result = text.innerText
//do some other work
} else {
result = "not found";
}

Related

How to remove generated node on page

I'm creating a node manually using vanilla JS. The idea is this alert appears after an input field if the maxlength limit has been reached and is removed if it returns below. This is being used on a CMS that users can use to create forms dynamically, so I won't know if the field will have a maxlength or not, or if it will have anything else after it. I'm using the following code:
document.querySelectorAll('input, textarea').forEach(element => {
if (element.hasAttribute('maxlength')) {
let maxChars = element.getAttribute('maxlength');
let elID = element.getAttribute('id');
let charWarning = document.querySelectorAll('#' + elID + ' + .char-limit');
element.addEventListener('input', () => {
let inputLength = element.value.length;
console.log(inputLength);
if (inputLength >= maxChars) {
if (charWarning.length == 0) {
let divAlert = document.createElement('div');
let divAlertText = document.createTextNode(maxChars + ' character limit reached on input');
divAlert.classList.add('text-danger', 'char-limit');
divAlert.setAttribute('aria-live', 'polite');
divAlert.setAttribute('aria-labelledby', elID);
divAlert.appendChild(divAlertText);
element.insertAdjacentElement('afterend', divAlert);
charWarning = document.querySelectorAll('#' + elID + ' + .char-limit');
}
} else {
// console.log(charWarning.length);
if (charWarning.length > 0) {
charWarning.remove(); // This is not working and I have no idea why.
}
}
});
}
});
For whatever reason, the .remove() function isn't working. It's throwing an error:
charWarning.remove is not a function at HTMLTextAreaElement
I don't really understand this. I thought it might be down to the fact that the initial setting of the charWarning prior to the listener was static, so I've added it again at the end of the function creating the warning element. It all works fine, but it's not removing the warning when below the maxlength and throwing that error.
For info, the commented out console.log:
// console.log(charWarning.length);
When uncommented does return 1, when the node has been added.
Can anyone point to what I'm doing wrong?
Ok, so after 2 days of no real responses, I took a late night stab at this again and somehow figured it out through trial and error. I thought the issue was to do with scoping but I couldn't figure out WHAT scope. In the end, I tried changing the "let" to "var" on the warning and that didn't work. So then I changed the selector from document.querySelectorAll to document.querySelector, cleaned up a little of the code and removed the else by using conditional chaining and voilĂ ... result! Much cleaner, much better (and most importantly) functioning code...
However, on researching the properties of aria-live="polite" I discovered that creating and removing the node is the incorrect process. I can create it but when I want to update it for assistive technologies, it's the content I need to change. This kinda goes back to #ZainWilson-WCHStudent's first comment about showing and hiding the content. While I'm still not doing this as I believe it is incorrect for accessibility, it does kind of lean on that idea. I believe that this solution is much more elegant, efficient and (most importantly) accessible:
document.querySelectorAll("input, textarea").forEach((e) => {
if (e.hasAttribute("maxlength")) {
let maxChars = e.getAttribute("maxlength");
let elID = e.getAttribute("id");
let divAlert = document.createElement("div");
let divAlertText = document.createTextNode(
maxChars + " character limit reached on input"
);
divAlert.classList.add("text-danger", "char-limit");
divAlert.setAttribute("aria-live", "polite");
e.insertAdjacentElement("afterend", divAlert);
e.addEventListener("input", () => {
let charWarning = document.querySelector("#" + elID + " + .char-limit");
let inputLength = e.value.length;
if (inputLength >= maxChars) {
charWarning.appendChild(divAlertText);
} else {
if(charWarning.firstChild) {
charWarning.removeChild(charWarning.firstChild);
}
}
});
}
});
Here's a Codepen to test: https://codepen.io/tadywankenobi/pen/OJQXwwR

input box asking for two specific words

I'm new to this, so I hope I can explain well enough what my problem is.
I've got a quiz and for an answer I created an input box. To get to another link you have to put two words in there but the order should not matter aka. it shouldn't matter if you write down "word1 word2" or "word2 word1", there should be only one rule: both words should be mentioned.
Is that possible?
My code so far:
function checkText()
{
var textwunf_1 = document.getElementById("wunf").value;
if(textwunf_1.toLowerCase() == "word1" && "word2"){
window.open("URL","_self");
}
else{
xxx
}
}
It does not work.
Before I only wanted to check if one word is used, like that:
var textwunf_2 = 'word1';
function checkText()
{
var textwunf_1 = document.getElementById("wunf").value;
if(textwunf_1.toLowerCase().indexOf(textwunf_2) == -1){
xxx
}
else{
window.open("URL","_self");
}
}
This worked but I can't use it for two words, because if I write
var textwunf_2 = 'word1 word2';
the order can't be 'word2 word1'...
Is there a solution to my problem?
Hopefully anyone can understand and help me, thank you!
Based on this commentary from the OP:
if the user types 3 words and two of them match with the answer, it should be also okay! even better if even 3 words or more are possible, as long as the user puts my two words in it..
You can check if both words are whitin the text using two conditions on the if:
textwunf_1.toLowerCase().indexOf("word1") >= 0
AND
textwunf_1.toLowerCase().indexOf("word2") >= 0
Try with the next code:
var textwunf_2 = 'word1';
var textwunf_3 = 'word2';
function checkText()
{
var textwunf_1 = document.getElementById("wunf").value;
if ((textwunf_1.toLowerCase().indexOf(textwunf_2) >= 0) &&
(textwunf_1.toLowerCase().indexOf(textwunf_3) >= 0))
{
window.open("URL","_self");
}
else
{
// xxx
}
}
Another approach:
var words = ["word1", "word2"];
function CheckWords() {
var inputWords = document.getElementById("wunf").value.split(' ');
var allWordsFound = true;
if (inputWords.length !== words.length) { return false; }
inputWords.forEach(function(word) {
if (words.indexOf(word.toLowerCase()) === -1) {
allWordsFound = false;
return;
}
});
return allWordsFound;
}
console.log(CheckWords());
I am creating a function that receive the text and check if include the answers(xx and yy), it doesn't matter the order. The ans list, can have 1,2 or more words, it will work.
let ans = ['xx','yy'];
function check(text){
text = text.toLowerCase();
let counter = 0;
ans.forEach((x) => {text.includes(x) && counter++ })
return counter === ans.length
}
console.log(check("aa bb")) // false
console.log(check("xx bb")) // false
console.log(check("aa yy")) // false
console.log(check("xx yy")) // true
console.log(check("yy xx")) // true

JavaScript regular expression having a weird behaviour

I'm currently making a scripting language for a Discord bot I'm maintaining and I'm facing a weird issue. The following code takes a string as input (I think {if:3|=|0|you are|TechRax is} {range:1|100}), uses the match method of the string to get all functions (expression: /\{(.*?):(.*?)\}/g) from the string. Then using a forEach, I process all of these matches then I replace the matched content with the result on the string, using the replace method.
Here is the code I use:
let newString = 'I think {if:3|=|0|you are|TechRax is} {range:1|100}';
const functionPattern = /\{(.*?):(.*?)\}/g;
const foundFunctions = newString.match(functionPattern);
if (!foundFunctions) throw new Error('No function found');
foundFunctions.forEach((fn) => {
const parsedInput = functionPattern.exec(fn); // = null once the second iteration begins... ? only the first one work. Same issue if I invert the function orders (first works, second and + no)
if (!parsedInput || !parsedInput[1] || !parsedInput[2]) return;
try {
/*const customFunction = new (require(`../../Production/Tags/${parsedInput[1]}`))(this.client, context, contextType);
if (!customFunction) return;
const result = customFunction.run(parsedInput[2].split('|'));*/
const result = 'Stack Overflow test';
newString = newString.replace(fn, result);
} catch (e) {
newString = newString.replace(fn, e);
}
});
// Print newString here (depends if you're on browser or node)
In this context, this.client.constants.functionPattern = /\{(.*?):(.*?)\}/g, foundFunctions = ['{if:4|=|0|you are|alien is}', '{range:1|100}'] and newString = 'I think {if:{argslen}|=|0|you are|{args} is} {range:1|100}'.
Now let's start describing the behaviour, the first iteration goes well: the function module gets imported, it gets processed and the final content gets replaced on the string.
The problem concerns the second one (and all others), the exec method of the function expression returns null. I do not understand this at all, first I thought it was a kind of bug with my RegExp, maybe {random:1|100} was not matching but no because it works perfectly on Regexr.com and... the weirdest: if I eval it (/\{(.*?):(.*?)\}/g.exec('{range:1|100}), it doesn't return null but the actual result I expect.
I guess I'm wrong somewhere but after passing some hours on it I still do not get why it isn't working.
I hope you'll be able to help me out, thanks!
If you need any complementary information, I'm here.
The problem is you're defining your regex GLOBAL
but don't reset the internal pointer inside of the loop: myRegex.lastIndex = 0; (see MDN)
alternatively, you could recreate a regex inside of the forEach.
let newString = 'I think {if:3|=|0|you are|TechRax is} {range:1|100}';
let functionPattern = /\{([^}]*):([^}]*)\}/g;
const foundFunctions = newString.match(functionPattern);
if (!foundFunctions)
throw new Error('No function found');
foundFunctions.forEach(fn => {
//const functionPattern = /\{([^}]*):([^}]*)\}/g; // or redeclare
const parsedInput = functionPattern.exec(fn);
if (!parsedInput || !parsedInput[1] || !parsedInput[2]) return;
try {
const result = 'Stack Overflow test';
newString = newString.replace(fn, result);
functionPattern.lastIndex = 0; // reset internal pointer of your regex
} catch (e) {
newString = newString.replace(fn, e);
}
});
console.log(newString);
I almost forgot: I suggest a more robust regex pattern: \{(\[^}\]*):(\[^}\]*)\}
However, your pattern seems to be good enough.

Promises and for loops - trying to validate multiple element text

I am working on a function that will read the text of elements after using a filter feature. I have printed out the returned text and it is getting the elements, however I do not think I understand js promises.. activeFilters is a var I have already identified.
this.verifyColorFilterFunctional = function(color) {
var bool = true;
activeFilters.count().then(function (count) {
var amt = count - 1;
for (var i = 0; i < amt; i++){
activeFilters.get(i).getText().then(function(text) {
bool = (color === text);
console.log(bool);
});
if (!bool) {
break;
}
}
});
return expect(bool).to.become(true);
};
The console.log prints out true and false as desired, however there are two things I have noticed. When false, it doesnt break like I told it to in the if statement. Also, I am getting a typeError: true is not a thenable error.. I believe the logic sounds good in my head but not to JS. Any help would be greatly appreciated.
Protractor's element.all() supports getText() method which will return you the text displayed in the elements as an array.Then you can easily compare the resultant array using expect method.
this.verifyColorFilterFunctional = function(color) {
activeFilters.getText().then(function (textArray) {
expect(textArray).to.equal(Array(textArray.length-1).fill(color));
});
}

Getting the length of a textbox value or content using JavaScript

This is something that's driving me nuts:
I have this code and it works: I am trying to learn JavaScript before becoming addicted to JQuery. My sample project involves getting the value of the text-box, and validating according to it's length. the name of the form is membership.
Example: This works:
function validateForm()
{
var element = document.membership;
if(element.txtName.value == "")
{
element.txtName.className = "alert";
}
else
{
element.txtName.className = "";
}
}
But this doesn't:
function validateForm()
{
var element = document.membership;
var nameLenght = element.txtName.value.lenght;
if(nameLenght < 1)
{
element.txtName.className = "alert";
}
else
{
element.txtName.className = "";
}
}
Just an FYI: I am new to JavaScript but very familiar with the syntax. I just want to learn the basics and move up.
I even read some solutions on here but feel I am simply sinking deeper.
Thanks for your help.
May be it is just because of typo in length here:
element.txtName.value.lenght;
must be element.txtName.value.length;.
If you want it to run every time user presses key , then look here: How to check string length with JavaScript
you can use this function as well
var a = txtName.value;
if (a.length < 1) {
alert('your message');
return false;
}

Categories

Resources