jquery append performed link replace - javascript

inputTabTitle: function(){
origin = template.clone();
$("#inputTabCount").change(function(){
tabcount = parseInt($("#inputTabCount").val());
if(tabcount > 0){
tab = origin.find("label").text();
for(i = 1; i <= tabcount; i ++){
origin.find("label").text(tab + i);
origin.find("label").attr("for", "inputTabTitle" + i);
origin.find("input").attr("id", "inputTabTitle" + i);
$("#tabCount").append(origin);
}
}
})
}
set n = 3
When append to "#tabCount", only one element insert, actually should be three.But this code append performed like replace.Why?
And when I add "origin = origin.clone()" before loop end, it worked well, three element inserted.

You clone your template only once. That means: Two times you append the 'origin' to a place, where it already is.
To get, what you want (or I think you want), the cloning MUST be in the loop.
Please notice further that you pollute the GLOBAL space when you define variables such as 'tabcount' without the 'var'. I fixed that in your source code, too.
Rewrite the function like that below.
But be warned: The amount of tabs is being inserted every time the value changes. That means:
Value changes to 1 --> one tab is made
Value changes to 2 --> two ADDITIONAL tabs are made.
.
inputTabTitle: function(){
$("#inputTabCount").change(function(){
var tabcount = parseInt($("#inputTabCount").val());
if(tabcount > 0){
tab = template.find("label").text();
for(i = 1; i <= tabcount; i ++){
var origin = template.clone();
origin.find("label").text(tab + i);
origin.find("label").attr("for", "inputTabTitle" + i);
origin.find("input").attr("id", "inputTabTitle" + i);
$("#tabCount").append(origin);
}
}
})
}

Related

How to loop through functions by name in P5js / JavaScript

I'm working on a basic P5 program that requires up to ten input boxes.
So I need to first create the button instances, using e.g.
factor1Input = createInput(""); // create DOM element for input field
factor1Input.position(leftMargin, topMargin + 50); // place button on screen
factor1Input.changed(this.factor1update); // call function when value changes
factor1Button = createButton('Update Factor'); // create DOM element for button
factor1Button.position(100, 100); // position button
Then toggle their visibility using e.g.
factor1Input.show(); // toggle display on
factor1Button.show();
factor1Input.hide(); // or toggle it off
factor1Button.hide();
But because I'll have up to 10, this will require a ton of repetitive code.
So I want to create a loop that goes something like (e.g. just for the show function);
for (let i = 1; i < factorCount; i++){
let fci = "factor" + i + "Input";
let fcb = "factor" + i + "Button";
fci.show();
fcb.show();
}
But I'm getting the following error:
Uncaught TypeError: fci.show is not a function
Which suggests some kind of type mismatch, i.e. I can't seem to just compile a string, and have this recognized as the JavaScript function.
Any suggestions?
fci will be a string so the String class will not have a method show, You will get an exception. Instead
You can write in this way
var factorObject = {
factor1Input:createInput("")
}
factorObject['factor1Input'].position(leftMargin, topMargin + 50);
For show
factorObject['factor1Input'].show();
Here in the loop
for (let i = 1; i < factorCount; i++){
let fci = "factor" + i + "Input";
factorObject[fci].show();
}
The reason why your code doesn't work is because you create a string and try to call it like a variable with the same name
let fci = "factor" + i + "Input";
fci.show(); // fci is just a string 'factor1Input', has nothing in common with factor1Input variable
You should use arrays Arrays and instead of keeping input number (e.g. index) inside a name, let it be the index of an item in the array
const factorCount = 10
const inputs = []
const buttons = []
// example of creating inputs and buttons in a loop, you can create them manually if you want,
// but don't forget to .push them to respective array
for (let i = 1; i < factorCount; i++){
const input = createInput("");
input.position(leftMargin, topMargin + 50 * i); // using index to calculate top margin
input.changed((value) => this.factorUpdate(i, value)); // notice the change here
inputs.push(input)
const button = createButton('Update Factor');
button.position(100, 100 + 50 * i); // also using index to calculate top margin
buttons.push(button)
}
function showInput(index) {
inputs[index].show()
buttons[index].show()
}
function hideInput(index) {
inputs[index].hide()
buttons[index].hide()
}
showInput(3) // shows 3rd input and button
hideInput(4) // hides 4th input and button
Notice also how I changed your this.factor1update method call. The same way you don't want to have 10 separate variables for 10 elements, you don't want to have 10 methods to handle changes on those 10 elements (what if there was 10000 elements?). Instead, create one method factorUpdate that will receive item index and the value that was changed and use that to handle the input change
added:
for (let i = 0; i < factorCount; i++){ // changed 1 to 0 here, it was a typo
const input = createInput("");
input.position(leftMargin, topMargin + 50 * i);
input.changed(() => factorUpdate(i)); // we call factorUpdate with index of an element
inputs.push(input)
const button = createButton('Update Factor');
button.position(185, topMargin + 50 * i);
buttons.push(button)
}
function factorUpdate(i, event){
// argument i is now an index of unfocused input
console.log("input index: " + i + ", value: " + inputs[i].value());
}
Note also how input.changed() works: you edit the input, then you click somewhere else on the page to unfocus it, and that's when this event is triggered. With that in mind, buttons here don't actually do anything as there are no click listeners assigned to them

counter variable is holding 2 values on 2nd pass of the function

I'm making a typing game. When multiple players play the game it runs through the same set of functions again. I'm using the variable j as a counter to advance words when they are typed correctly. For some reason, on the second pass on each upkeystroke, it logs j = 1 & j = whatever the value of the previous players last word + 1 is. When each player plays, I want each set of words they are typing to be the same, so that it is fair. I can't figure out why this is happening or even how the variable has 2 values at the same time?!?!?
What gives?
Here's the code in question, but there's a bunch of callbacks that could be involved, although the only place this variable is called is inside this function.
//advances ship on correct typing
function runRace() {
timer();
var j = 1;
//BUG HERE !! Works fine on first iteration but on second
//iterations value jumps beteween 1 and whatever the next
//one is. It's like on every keystroke it reassigns var j
//back to 1, then back to the element number it was on
//last time
//!!! j has 2 values !!!it's keeping the value from the
//prior running of run race
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
}
If you need to see the rest of the code to figure this out, it's here. Eventhough it's not running right on jsfiddle for reason, it works other then the bug, locally https://jsfiddle.net/ujsr139r/1/
As i mentioned in my comment you're creating multiple listeners everytime runRace() is called.
You could try something like this instead (please note, this isn't the best way to do this, i'm just demoing. Global variables like j in this case aren't a clever idea.:
var j=1; // global because its outside of your function
$(function(){
$(document).keyup(function(e){
var targetWord = $(".toType").text();
var typedWord = $("#word").val();
//while (j < gameWords.length){
console.log("j = " + j);
if(typedWord === targetWord){
$(".player").css({left: "+=15px",});
targetWord = $(".toType").text(gameWords[j]);
$("#word").val("");
j++;
}else {
return
};
//}
});
});
//advances ship on correct typing
function runRace() {
j = 1;
timer();
}

Simplify IF...Else Statement with For Loop [Javascript]

Context: I am creating a table of content to inform user of the page(s) that they have completed/visited.
The if...else statement below is working (by itself) but i want to generate the check for the other 30 chapters using the "i" counter from the FOR loop.
The script will first load the localstorage that contains the value 1 or 0 representing visited / unvisited and transfer that data onto variable chap1check. based on the result, the table of content should then show the user which page have or have not been visited.
im not sure of what i need to do but in theory, i will need to replace all the "chap1..." with the value of "i".
<script type="text/javascript">
var i;
for (i = 1; i <=31; i++){
var chap1Check = localStorage.getItem("chap1Status");
if(chap1Check == "1"){
document.getElementById('chap1Completion').innerHTML = "Completed";
}else{
document.getElementById('chap1Completion').innerHTML = "Incomplete";
}
}
</script>
Just concatenate the part of the string before the number with the number (i), followed by the part of the string after the number.
for (var i = 1; i <= 31; i ++){
var chapCheck = localStorage.getItem("chap" + i + "Status");
document.getElementById('chap' + i + 'Completion').textContent = chapCheck == "1" ? "Completed" : "Incomplete";
}
The following code will work. However, it would be much cleaner for you to just store an Array in local storage, and access it by indexing the array. Also, take a look into using the map functor over a for loop.
Note also that you should inline the declaration of i in the for loop as shown below. Otherwise, you may get conflicts with any future use of i in a for loop.
for (var i = 1; i <=31; i++){
var chapterChecker = localStorage.getItem("chap"+i+"Status");
if(chap1Check == "1"){
document.getElementById('chap'+i+'Completion').innerHTML = "Completed";
}else{
document.getElementById('chap'+i+'Completion').innerHTML = "Incomplete";
}
}
A solution using es6 template string could be this
for (var i = 1; i <=31; i++){
let content = '';
if(localStorage.getItem(`chap${i}Status`) == "1"){
content = "Completed";
}else{
content = "Incomplete";
}
document.getElementById(`chap${i}Completion`).innerHTML = content;
}

JQuery for loop stuck at last index [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
jQuery Looping and Attaching Click Events
(4 answers)
Closed 9 years ago.
I have function process_row that appends tags to html, and those tags are chained to a function upon clicked. (in this case, simply alert(i), its position in the result array).
But however, upon being clicked, the newly generated alerts the length of the entire result array. I have tried many, many changes to try and make it work, but it doesn't.
Strange thou, fab_div.attr("id", result_data[0]); works fine !! In Chrome inspect element the id tags are displayed as they are, but the click function points everything to the last element in the array.
for example, if I do, fab_div.click(function () { alert(result_data[0]) });, I get the name of the LAST element in the array, doesn't matter which element was clicked.
can anyone please explain to me... WHY??
I think it may have something to do with $("<div>") where JQuery thinks it's the same div that it's assigning to. Is there any way around this? The 's are generated dynamically and I would not want to let PHP do the echoing. Plus the content may be updated realtime.
Example dataset :
Smith_Jones#Smith#Jones#janet_Moore#Janet#Moore#Andrew_Wilson#Andrew#Wilson
After many, many changes, still not working:
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>");
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
fab_div.append(fab_text)
// fab_div.click(function () { alert(i) });
// ^ not working, try appending list of id's to id_list
id_list.push(result_data[0])
$('#ls_admin').append(fab_div)
}
for(j = 0; j < id_list.length; j++){
$('#' + id_list[j]).click(function () { alert(j) })
}
}
}
Original Attempt:
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>").append(fab_text).click(function () { alert(i) });
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
$('#ls_admin').append(fab_div)
}
}
}
If you must use an alert, then you can encapsulate the click handler in a self executing function and pass the index to it. Like,
(function (index) {
fab_div.click(function () {
alert(index);
});
})(i);
Although, this is not a clean way to do it. Otherwise, if you are looking to just manipulate the div element is any way, then adding any method directly will also work. Like,
fab_div.click(function () {
alert($(this).attr('id'));
});
You can refer a jsFiddle here
Wonky Solution, but it worked! Haha! Big thanks to Kevin B.
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>").append(fab_text);
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
$('#ls_admin').append(fab_div)
}
$("#ls_admin").children(this).each(function( index ) {
$(this).append($(this).click(function () { alert($(this).text()) }));
});
}
}

JavaScript div ordering script bug (switches order of the two divs below)

Code:
http://jsfiddle.net/s4UQP/
^ Here is the best way to see the code and how it works with the divs
But here is the code anyway:
function move(from, to) {
document.getElementById('progress').innerHTML = '...';
from = parseInt(from,10);
to = parseInt(to,10);
tbc = document.getElementById(from);
before = document.getElementById(to);
containr = document.getElementById('alldivs');
neworder = 'Order: <select><option onclick="move(' + to + ',1)">1</option><option onclick="move(' + to + ',2)">2</option><option onclick="move(' + to + ',3)">3</option></select> <br>Send up | Send down<br>Bring to front (#1) | Send to back (#4)';
document.getElementById(from).getElementsByClassName('order')[0].innerHTML = neworder;
document.getElementById(from).getElementsByClassName('number')[0].innerHTML = to;
tempdiv = document.createElement('div');
tmphtml = document.getElementById(from).innerHTML;
tempdiv.className = 'holder';
tempdiv.innerHTML = tmphtml;
n = 0;
npieces = 4;
if (from < to) {
nochanges = to - from;
fromone = from + 1;
//alert(n+' '+to+' '+fromone);
for (n = fromone; n <= to; n++) {
//alert('down');
idnum = parseInt(document.getElementById(n).id,10);
//alert(idnum);
document.getElementById(n).getElementsByClassName('number')[0].innerHTML = (idnum - 1);
alert(document.getElementById(n).id);
document.getElementById(n).id = (idnum - 1);
//alert('down '+idnum+' to '+(idnum-1));
}
}
if (from > to) {
nochanges = from - to;
totone = to + 1;
for (n = to; n < from; n++) {
//alert('n is '+n+' going to '+to+' ends at '+totone);
//alert('up');
idnum = parseInt(document.getElementById(n).id,10);
//alert(idnum);
document.getElementById(n).getElementsByClassName('number')[0].innerHTML = (idnum + 1);
alert(document.getElementById(n).id);
document.getElementById(n).id = (idnum + 1);
//alert('up '+idnum+' to '+(idnum+1));
}
}
//tempdiv.id = 'span'+to;
if (from > to) {
containr.insertBefore(tempdiv, before);
}
if (from < to) {
before = to + 1;
containr.insertBefore(tempdiv, document.getElementById(before));
}
tbc.parentNode.removeChild(tbc);
tempdiv.id = to;
document.getElementById('progress').innerHTML = 'done';
}
The script works as you move a block (or div) up or down, but when you try to move a different block (e.g. the one at the top), it just switches around the first two blocks beneath it.
Could anyone give me any advice?
I don't know whether it's because of the order that the script was done in, or if it's something else. It's been confusing me for some time, and I'd really appreciate it if someone could look through it and give me some advice.
(I don't want to code it in jQuery, this is really just me trying to learn more JavaScript by coding something. If it's not the most efficient, secure, whatever, it's still just something with which I'm trying to teach myself JavaScript.)
Thank you for reading. (Please don't edit the JS Fiddle itself, but rather post any edits/improvements here. Thank you.)
[Edit: I'm not really writing a cliche sci-fi, they're just example divs because I couldn't think of anything better]
In the statement neworder =... you change the values of the onclick functions, but you only do this for the block that is about to be moved. The problem is that the other blocks also change positions. For instance, if you click on 'Send up' for block 2, then block 2 moves up to position 1 and block 1 moves down to position 2. But only the event handlers on block 2 are updated accordingly. So the next time you click on (what was originally) block 1, it will not behave correctly.
One solution would be to update the event handlers on all of the blocks that are affected every time one of them is moved. For instance, make a function called updateEventHandlers(blockNumber) and call it for all of the affected blocks.
However relying on IDs to indicate the position of a block and then fiddling with the IDs after they are moved can lead to all sorts of confusion. It is better either to keep an array or dictionary recording the positions of the blocks, or loop through them to determine their positions in the DOM each time you want to move them.
For instance the following code provides moveup, movedown and moveto functions using the latter method (it finds where the element is in the DOM and swaps it with the holder before or after). (JSFIDDLE)
function E(id) { return document.getElementById(id);}
var holders = document.getElementsByClassName('holder');
function moveup(id) {
for (var i = 0; i < holders.length - 1; i++) {
// Find the holder before the one we're interested in
if (holders[i + 1] == E(id)) {
// Swap their positions
E('alldivs').insertBefore(E(id), holders[i]);
break;
}
}
resetNumbers();
}
function movedown(id) {
for (var i = 1; i < holders.length; i++) {
// Find the holder after the one we're interested in
if (holders[i - 1] == E(id)) {
// Swap their positions
E('alldivs').insertBefore(holders[i], E(id));
break;
}
}
resetNumbers();
}
function moveto(id, position) {
if (position == holders.length) { // move to end
E('alldivs').appendChild(E(id));
}
else { // move before another holder
E('alldivs').insertBefore(E(id), holders[position - 1]);
}
resetNumbers();
}
function resetNumbers() {
// Reset all the numbers to reflect their current position
var numbers = document.getElementsByClassName('number');
for (var i = 0; i < numbers.length; i++) {
numbers[i].innerHTML = i + 1;
}
}​
A few other points:
clicking on the selects in your original code won't do anything initially, because no event handler is assigned to it until after one of the elements has been moved
there is a missing </div> from the end of the html
it is good practice to declare variables using var somewhere in your code
appendChild and insertBefore remove a node from its current position in the DOM before appending/inserting it in its new position, so there is no need to remove the element explicitly.
having moveup and movedown functions is better than only having moveto, which requires you to insert the current, preceding and following positions into the html and refresh them every time a block is moved.

Categories

Resources