I have an script that prints each letter of an string and how many times it is printed, but it is not working properly, it overrides the last letter.
For example: when you type in the textarea "assist" it should print the following:
a: 1
s: 3
i: 1
t: 1
But it only prints:
t: 1
How to print properly the string?
function ex44() {
let string = document.getElementById("sarc4").value
string = string.toLowerCase();
const count = {}
string.split("").forEach(ch => {
if (!count[ch]) {
count[ch] = 1
return
}
count[ch]++
})
for (const key in count) {
document.getElementById("result4").innerHTML = `${key} : ${count[key]}`
}
}
<label for="sarcina4"> Enter text</label>
<br>
<textarea name="sarcina4" id="sarc4" cols="60" rows="5"></textarea>
<br>
<button onclick="ex44()">Afisare</button>
<p id="result4"></p>
document.getElementById("result4").innerHTML = `${key} : ${count[key]}`
Will override the innerHTML.
Consider 'adding' the content using appendChild or insertAdjacentHTML:
Is it possible to append to innerHTML without destroying descendants' event listeners?
const res = document.getElementById("result4");
for (const key in count) {
res.insertAdjacentHTML('beforeend', `<p>${key} : ${count[key]}</p>`);
}
function ex44() {
let string = document.getElementById("sarc4").value
string = string.toLowerCase();
const count = {}
string.split("").forEach(ch => {
if (!count[ch]) {
count[ch] = 1
return
}
count[ch]++
})
const res = document.getElementById("result4");
for (const key in count) {
res.insertAdjacentHTML('beforeend', `<p>${key} : ${count[key]}</p>`);
}
}
<label for="sarcina4"> Enter text</label>
<br>
<textarea name="sarcina4" id="sarc4" cols="60" rows="5"></textarea>
<br>
<button onclick="ex44()">Afisare</button>
<p id="result4"></p>
Note: I've wrapped the ${key} : ${count[key]} inside a <p></p> tag so we see each letter on a separate row
Another option is to create an output string by using map and join like so:
const output = Object.keys(count).map((key) => `<p>${key} : ${count[key]}</p>`);
document.getElementById("result4").innerHTML = output.join('');
function ex44() {
const input = document.getElementById("sarc4").value.toLowerCase();
const count = {}
input.split("").forEach(ch => {
if (!count[ch]) {
count[ch] = 1;
} else {
count[ch]++
}
});
const output = Object.keys(count).map((key) => `<p>${key} : ${count[key]}</p>`);
document.getElementById("result4").innerHTML = output.join('');
}
<label for="sarcina4"> Enter text</label>
<br>
<textarea name="sarcina4" id="sarc4" cols="60" rows="5">test</textarea>
<br>
<button onclick="ex44()">Afisare</button>
<p id="result4"></p>
You should use Object.keys(count) to foreach. And currently, you are overwriting the result's HTML on loop. So I use a newHtml variable to save it and set it into innerHTML outside loop.
function ex44() {
let string = document.getElementById("sarc4").value
string = string.toLowerCase();
const count = {}
string.split("").forEach(ch => {
if (!count[ch]) {
count[ch] = 1
return
}
count[ch]++
})
var newHtml = '';
for (const key of Object.keys(count)) {
newHtml += `${key} : ${count[key]} <br/>`
}
document.getElementById("result4").innerHTML = newHtml;
}
<label for="sarcina4"> Enter text</label>
<br>
<textarea name="sarcina4" id="sarc4" cols="60" rows="5"></textarea>
<br>
<button onclick="ex44()">Afisare</button>
<p id="result4"></p>
Move the document.getElementById("result4").innerHTML outside the loop.
You are rewritting the innerHTML of the element with id result4 each time the loop is executing. You can update the innerHTML by appending the new striing with existing by using below to get the issue fixed.
document.getElementById("result4").innerHTML += `${key} : ${count[key]}`
The above implementation cannot be considered as a good implementation even though it's fixes your problem. Accessing DOM elements directly from script is always costly. If it's inside a loop a loop it's definitely more costly.
Instead of accessing the DOM element multiple time from a loop, you could create the template string inside the loop and assign the innerHTML only once as below. This will be better in terms of performance.
function ex44() {
let string = document.getElementById("sarc4").value
string = string.toLowerCase();
let template = '';
const count = {}
string.split("").forEach(ch => {
if (!count[ch]) {
count[ch] = 1
return
}
count[ch]++
})
for (const key in count) {
template += `${key} : ${count[key]}`
}
document.getElementById("result4").innerHTML = template;
}
<label for="sarcina4"> Enter text</label>
<br>
<textarea name="sarcina4" id="sarc4" cols="60" rows="5"></textarea>
<br>
<button onclick="ex44()">Afisare</button>
<p id="result4"></p>
Cache your elements, and then use textContent to update the element text using string concatenation.
const sarc4 = document.getElementById('sarc4');
const result4 = document.getElementById('result4');
function ex44() {
string = sarc4.value.toLowerCase();
const count = {};
string.split("").forEach(ch => {
if (!count[ch]) {
count[ch] = 1;
return;
}
count[ch]++
});
let result = '';
for (const key in count) {
result += `${key}:${count[key]} `;
}
result4.textContent = result;
}
<label for="sarcina4"> Enter text</label>
<br>
<textarea name="sarcina4" id="sarc4" cols="60" rows="5"></textarea>
<br>
<button onclick="ex44()">Afisare</button>
<p id="result4"></p>
Related
I have this code where I want to add text to the select box when calling a function via clicking an input button.
I want the select box to have a default text when the page is loaded and no value is added to the array. And I want this text to vanish but I could still add many values from the input box and make them show on the select box.
So I made the input and select box with the following:
let num = document.querySelector('input#numtxt')
let lista = document.querySelector('select#seltxt')
let res = document.querySelector('div#res')
let valores = []
function adicionar() {
if (isNumero(num.value) && !inLista(num.value, valores)) {
lista.options[0] = null //
valores.push(Number(num.value))
let item = document.createElement('option')
item.text = `Valor ${num.value} adicionado.`
lista.appendChild(item)
} else {
window.alert('Valor inválido ou já existe!')
}
}
<div>
<p>TYpe a number between 1 and 100: <input type="number" name="num1" id="numtxt">
<input type="button" value="Adicionar" onclick="adicionar()"></p>
</div>
<div>
<p>
<select name="sel1" id="seltxt" size="10">
<option>Type a number above!</option>
</select>
</p>
<p><input type="button" value="End" onclick="finalizar()"></p>
</div>
I've tried a lot of commands with boxvar.options[0] = null and boxvar.remove(0)but they all kept removing the first value which I need for the program.
Any sugestions?
let num = document.querySelector('input#numtxt')
let lista = document.querySelector('select#seltxt')
let res = document.querySelector('div#res')
let valores = []
function adicionar() {
if (isNumero(num.value) && !inLista(num.value, valores)) {
if(!valores.length) {
// If there are no values on list, delete whatever is inside of select
lista.innerHTML = ''
}
valores.push(Number(num.value))
let item = document.createElement('option')
item.text = `Valor ${num.value} adicionado.`
lista.appendChild(item)
} else {
window.alert('Valor inválido ou já existe!')
}
}
This is slightly verbose for clarity - if we add a data attribute we can filter on that and remove it if it exists. We can also filter by values and not add if the new one exists (it could be a data attribute if you do not want to set the value.
let lista = document.querySelector('#seltxt');
let res = document.querySelector('#res');
let valores = [];
function adicionar() {
let num = document.querySelector('#numtxt');
let opts = [...lista.options].filter((element, index) => {
return element.dataset.default == "default";
});
console.log(opts);
if (opts.length) {
opts[0].remove();
}
let newValue = Number(num.value);
// now if it already exists, don't add it
let matchOpt = [...lista.options].filter((element, index) => {
return element.value == newValue;
});
// we already have it so jump back out
if (matchOpt.length) {
return;
}
valores.push(newValue);
let item = document.createElement('option');
item.text = `Valor ${num.value} adicionado.`;
item.value = newValue;
lista.appendChild(item);
}
<div>
<p>Type a number between 1 and 100: <input type="number" name="num1" id="numtxt">
<input type="button" value="Adicionar" onclick="adicionar()"></p>
</div>
<div>
<p>
<select name="sel1" id="seltxt" size="10">
<option data-default="default">Type a number above!</option>
</select>
</p>
<p><input type="button" value="End" onclick="finalizar()"></p>
</div>
I'm in the learning stages, can you please give me the answer with the explanation.
Also if possible, I would appreciate it if you could explain to me what I have done wrong here and then also show me the better way of doing it.
//ARROW FUNCTION
const reverseNumber = num => (num.toString().split("").reverse().join(""));
let textAreaEl = document.querySelector('#text-area');
let submitButtonEl = document.querySelector('#output-button');
let outputAreaEl = document.querySelector('#output');
submitButtonEl.addEventListener("click", () => {
textAreaValue = reverseNumber(textAreaEl)
outputAreaEl.innerHTML = textAreaValue.value;
});
<form action="">
<label for="object" title="object">JavaScript function that returns a passed string with letters in alphabetical order</label>
<textarea id="text-area" name="object-name" id="" cols="30" rows="5"></textarea>
<input type="button" value="RETURN" id="return-value" />
<div id="output"></div>
</form>
First of all, there is no element with #output-button. You need to fix that.
You are passing the element itself to the reverseNumber(), you should pass the value to function.
I will also suggest you use innerText or textContent instead of innerHTML if the text is plain text (not htmlString).
Demo:
const reverseNumber = num => (num.toString().split("").reverse().join(""));
let textAreaEl = document.querySelector('#text-area');
let submitButtonEl = document.querySelector('#output-button');
let outputAreaEl = document.querySelector('#output');
submitButtonEl.addEventListener("click", () => {
textAreaValue = reverseNumber(textAreaEl.value); //pass value here
outputAreaEl.textContent = textAreaValue; //no need to use value here
});
<form action="">
<label for="object" title="object">JavaScript function that returns a passed string with letters in alphabetical order</label>
<textarea id="text-area" name="object-name" id="" cols="30" rows="5"></textarea>
<input type="button" value="RETURN" id="output-button"/> <!--fix the id here-->
<div id="output"></div>
</form>
Your script should be like this
const reverseNumber = num => (num.toString().split("").reverse().join(""));
console.log(reverseNumber(54321));
let textAreaEl = document.querySelector('#text-area');
let submitButtonEl = document.querySelector('#return-value');
let outputAreaEl = document.querySelector('#output');
submitButtonEl.addEventListener("click", () => {
let textAreaValue = reverseNumber(textAreaEl.value);
outputAreaEl.innerHTML = textAreaValue;
});
First, your document.querySelector('#output-button') is not match with <input type="button" value="RETURN" id="return-value"/>.
Second, you have to use variable declaration keyword to get textAreaEl.value
First of all textarea id is incorrect so querySelector is returning undefined and click event is not attached. I have corrected the button id in html.
You need to use textAreaEl.value to find the textarea text and pass it to reverseNumber function.
//ARROW FUNCTION
const reverseNumber = num => (num.toString().split("").reverse().join(""));
let textAreaEl = document.querySelector('#text-area');
let submitButtonEl = document.querySelector('#output-button');
let outputAreaEl = document.querySelector('#output');
submitButtonEl.addEventListener("click", () => {
textAreaValue = reverseNumber(textAreaEl.value)
outputAreaEl.innerHTML=textAreaValue;
});
<form action="">
<label for="object" title="object">JavaScript function that reverses a number</label>
<textarea id="text-area" name="object-name" id="" cols="30" rows="5"></textarea>
<input type="button" value="RETURN" id="output-button"/>
<div id="output"></div>
</form>
This is the Java Script I'm using. I can enter in the proceduredate and it copies to proceduredate2 but not 3 and 4--Same with Procedure
function copyTextAreaValue(id, ids) {
// get source element
var sourceElement = document.getElementById(id);
if(sourceElement) {
// copy to destination elements
var destIds = ids.split(',');
for(i=0; i<destIds.length; i++) {
var destEle = document.getElementById(destIds[i]);
if(destEle) {
destEle.value = sourceElement.value;
} else {
console.log('no dest element ' + destIds[i]);
}
}
}
}
Link to JSFiddle with full code
Your question id not clear to me. However, all I understood is you want to copy text from your current text input to other three text input. If that is true, then you can have your solution here. Please, checkout the snippet below.
let el = document.getElementById('field_1');
function copyTextValue() {
let elVal = el.value;
document.getElementById("field_2").value = elVal;
document.getElementById("field_3").value = elVal;
document.getElementById("field_4").value = elVal;
}
copyTextValue();
el.oninput = copyTextValue;
el.onchange = copyTextValue;
Today's Date: <input id="field_1" value=""/><br/>
Procedure Date: <input id="field_2" value=""/> <br/>
Date of Procedure: <input id="field_3" value=""/> <br/>
Surgery Date: <input id="field_4" value=""/> <br/>
function copyTextfieldValue(id, ids) {
// get source element
var sourceElement = document.getElementById(id);
if(sourceElement) {
// copy to destination elements
var destIds = ids.split(',');
for(i=0; i<destIds.length; i++) {
var destEle = document.getElementById(destIds[i]);
if(destEle) {
destEle.value = sourceElement.value;
} else {
//console.log('no dest element ' + destIds[i]);
}
}
}
}
<input id="t1">
<input id="t2">
<input id="t3">
<input id="t4">
<input type="button" value='copy' onclick="copyTextfieldValue('t1','t2,t3,t4');">
Your code seems to work fine. See the snippet.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 3 years ago.
i did this code so I could mirror textareas:
<body>
<textarea class="caixa1"></textarea>
<textarea class="caixa2"></textarea>
<br>
<textarea class="caixa1"></textarea>
<textarea class="caixa2"></textarea>
<script>
var a = document.getElementsByClassName("caixa1");
var b = document.getElementsByClassName("caixa2");
a[0].oninput = function(e) {
b[0].value = a[0].value;
}
b[0].oninput = function(e) {
a[0].value = b[0].value;
}
a[1].oninput = function(e) {
b[1].value = a[1].value;
}
b[1].oninput = function(e) {
a[1].value = b[1].value;
}
</script>
</body>
But I need to repeat this 8 times. Is there any way I could create a variable that changes according to the class index of the current textarea?
Iterate over each index of one of the collections, and access the same index on the other collection:
var a = document.getElementsByClassName("caixa1");
var b = document.getElementsByClassName("caixa2");
for (let i = 0; i < a.length; i++) {
const t1 = a[i];
const t2 = b[i];
t1.oninput = () => t2.value = t1.value;
t2.oninput = () => t1.value = t2.value;
}
<textarea class="caixa1"></textarea>
<textarea class="caixa2"></textarea>
<br>
<textarea class="caixa1"></textarea>
<textarea class="caixa2"></textarea>
An alternative would be to use event delegation on the container, and from the event target textarea, dynamically identify which is the linked adjacent textarea to assign a value to (either the next or previous sibling):
document.body.addEventListener('input', ({ target }) => {
const other = target[target.matches('.caixa1') ? 'nextElementSibling' : 'previousElementSibling'];
other.value = target.value;
});
<textarea class="caixa1"></textarea>
<textarea class="caixa2"></textarea>
<br>
<textarea class="caixa1"></textarea>
<textarea class="caixa2"></textarea>
Here is my Javascript code:
function validspan() {
var data = document.querySelector('.keyword-input').value;
var nodes = document.querySelectorAll('.keywords-list span');
var str = Array.prototype.map.call(nodes, function(node) {
return node.textContent;
}).filter(a => !!a).join(",");
var arr = str.split(',');
for (var k = 0; k < arr.length; k++) {
if (data == arr[k]) {
alert("Don't Enter Same Skill");
let list = document.querySelector('.keywords-list');
let array = arr[k].indexOf(data);
list.removeChild(list.childNodes[array]);
return true;
} else {
alert('different values');
return false;
}
}
}
and html code where runtime spans create
<input type="text" class="keyword-input with-border #error('name') is-invalid #enderror" name="skills" placeholder="Add Skills">
<div class="invalid-feedback" style="color: red;font-size: 20px"></div>
<button type="button" class="keyword-input-button ripple-effect" onclick="validspan()"><i class="icon-material-outline-add" ></i></button>
</div>
<div class="keywords-list">
<!-- keywords go here -->
</div>
If value match then delete index but in this my code only 0 index
check and delete 0 idex. my requirment is when same value any index
then delete index
i guess you are looking for something like this?
(() => {
const btnEl = document.querySelector('#add_skill');
const inputEl = document.querySelector('#skills');
const resultsEl = document.querySelector('#keywords-list');
const arr = [];
arr.add = function(val) {
this.push(val);
resultsEl.innerHTML = arr.map(item => `<span>${val}</span>`).join('');
};
btnEl.addEventListener('click', (e) => {
let val = inputEl.value;
if (!val) {
console.error(`A value is required`);
return;
}
if (arr.includes(val)) {
console.error(`Don't Enter Same Skill`);
return;
}
arr.add(val);
});
})();
<input type="text" class="keyword-input with-border #error('name') is-invalid #enderror" id="skills" name="skills" placeholder="Add Skills">
<div class="invalid-feedback" style="color: red;font-size: 20px"></div>
<button type="button" id="add_skill" class="keyword-input-button ripple-effect"><i class="icon-material-outline-add" ></i>Add</button>
</div>
<div class="keywords-list" id="keywords-list">
<!-- keywords go here -->
</div>
in your for loop, you are terminating the execution directly and breaking from it by using the return keyword in both the if statement and the else statement, so basically what your code is doing is just testing the first index (0) and omitting all the other indexes!
you need to remove the return statements and then continue editing your code to handle other cases!
you can understand better about return inside a for loop with the answers in this question : question
hope that helped!