I want to replace the content of the input entered by adding a "#" in front of it, but at the time of writing the spaces it does not work correctly.
function mayus(texto) {
var text = texto.value.replace('#', '');
var words = text.split(" ");
var newTexto = "";
for (var i = 0; i < words.length; i++) {
if (words[i].length > 0) {
newTexto += "#" + words[i];
} else {
newTexto += " ";
}
}
texto.value = newTexto;
};
<form class="form-horizontal" method="POST" action="addEvent.php">
<div class="form-group">
<label for="etiquetas" class="col-sm-2 control-label">Etiquetas</label>
<div class="col-sm-10">
<textarea name="etiquetas" class="form-control" id="etiquetas" placeholder="escriba y separe con espacio las etiquetas a usar" required onkeyup="mayus(this);" style="color: blue;"> </textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" data-dismiss="modal">Cerrar</button>
<button type="submit" class="btn btn-primary">Guardar</button>
</div>
</form>
We can simplify your function. First we look to see if the value already starts with a # using startsWith and if it does we return. If not, we then add it to the beginning.
function mayus(texto) {
let val = texto.value
let result = val.split(' ').map(item => !item.startsWith('#') && item != '' ? '#' + item : item)
texto.value = result.join(' ')
}
<form class="form-horizontal" method="POST" action="addEvent.php">
<div class="form-group">
<label for="etiquetas" class="col-sm-2 control-label">Etiquetas</label>
<div class="col-sm-10">
<textarea name="etiquetas" class="form-control" id="etiquetas" placeholder="escriba y separe con espacio las etiquetas a usar" required onkeyup="mayus(this);" style="color: blue;"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" data-dismiss="modal">Cerrar</button>
<button type="submit" class="btn btn-primary">Guardar</button>
</div>
</form>
Your else statement should not be there, and you can actually simplify your entire method using a more functional approach. Also use the input event instead of the keyup event to prevent a visual delay.
var previous
function mayus() {
var texto = this.value
.replace(/#/g, '') // removes ALL hashtags
.split(/\s+/g)
.filter(Boolean) // removes empty strings from array of words
.map(function(word) {
return '#' + word;
})
.join(' ');
// add space if key was pressed and output would have been identical
if (previous && texto === previous) {
texto += ' ';
}
previous = this.value = texto;
}
<form class="form-horizontal" method="POST" action="addEvent.php">
<div class="form-group">
<label for="etiquetas" class="col-sm-2 control-label">Etiquetas</label>
<div class="col-sm-10">
<textarea name="etiquetas" class="form-control" id="etiquetas" placeholder="escriba y separe con espacio las etiquetas a usar" required oninput="mayus.call(this);" style="color: blue;"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" data-dismiss="modal">Cerrar</button>
<button type="submit" class="btn btn-primary">Guardar</button>
</div>
</form>
Related
I want to hide the divi when I click the button and open it when I click it again. But I couldn't run the normally working code, what could be the reason?
The js code works when there is only one div, but it does not work due to this code I wrote, but I can't solve the problem.
Razor Page
#for (int i = 0; i < Model.quest.Count; i++)
{
<div class="row mt-12" >
<div class="col-md-1">
<input type="checkbox" id="questcb" asp-for="#Model.quest[i].check">
<span>#(i + 1) Soru</span>
</div>
<div class="col-md-9">
<textarea class="form-control" asp-for=#Model.quest[i].Question rows="3" id="question" hidden></textarea>
<label>#Model.quest[i].Question</label>
</div>
</div>
<div class="row mt-12">
<div class="col-md-1" hidden>
<button class="btn btn-outline-secondary" type="button" id="A" onclick="clickFunc(this.id)">A</button>
</div>
<div class="col-md-1" >
A)
</div>
<div class="col-md-11" hidden="hidden">
<input type="text" asp-for=#Model.quest[i].Answer1 class="form-control" placeholder="" id="answer"
aria-label="Example text with button addon" aria-describedby="button-addon1">
</div>
<div class="col-md-11" id="Amod_#i" style="display:inline-block">
#Model.quest[i].Answer1
</div>
<div class="col-md-2">
<button class="btn btn-primary" type="button" id="mod_#i" onclick="question(this.id)">Cevapları Görüntüle</button>
</div>
</div>
}
js code
let question = button => {
let element = document.getElementById(`A${button}`);
let buttonDOM = document.getElementById(`${button}`)
let hidden = element.getAttribute("hidden");
if (hidden) {
element.removeAttribute("hidden");
buttonDOM.innerText = "Cevapları Gizle";
}
else {
element.setAttribute("hidden", "hidden");
buttonDOM.innerText = "Cevapları Görüntüle";
}
}
</script>
If the id of div is unique in your code,your js should work.If it still doesn't work,you can try to find the div with the position of the button:
let question = button => {
let element = $("#" + button).parent().siblings(".col-md-11")[1];
let buttonDOM = document.getElementById(`${button}`);
if (element.hidden) {
element.removeAttribute("hidden");
buttonDOM.innerText = "Cevapları Gizle";
}
else {
element.setAttribute("hidden", "hidden");
buttonDOM.innerText = "Cevapları Görüntüle";
}
}
result:
I have a form with 5 questions, in which they are displayed one at a time to the user, as in a quiz, that is, the first question appears on the screen, the user answers and goes to the second, and so on.
However, I have a problem with questions 3 and 4, when I don't type anything and press ENTER, question 2 is also appearing on the screen, like the image below:
[![error in questions 3 and 4][1]][1]
[1]: https://i.stack.imgur.com/63pQC.png
I can't show two questions at the same time, just one at a time, and I don't know where I'm going wrong.
Here's the code inside the form tag:
<form class="questions_box formulario" action="enviar-landing-page-v3.php" method="post">
<div id="question-1">
<h3>The question is: ... ?</h3>
<input type="radio" name="objetivo" id="question-1-answer-a" value=1 required oninput="checkObjective()"> Answer 1.<br>
<input type="radio" name="objetivo" id="question-1-answer-b" value=2 oninput="checkObjective()"> Answer 2.<br>
<input type="radio" name="objetivo" id="question-1-answer-c" value=3 oninput="checkObjective()"> Answer 3.<br>
<div class="text-end mt-3">
<input type="submit" id="submit1" class="btn btn-primary btn-lg" value="Enviar Resposta" style="display: none;" />
</div>
<script type="text/javascript">
function checkObjective() {
if (document.getElementsByName('objetivo').value == '') {
document.getElementById('submit1').style.display = 'none';
} else {
document.getElementById('submit1').style.display = 'block';
}
}
</script>
</div>
<div id="question-2">
<h3>Quantos anos tem seu filho(a)?</h3>
<div class="input-formulario">
<i class="far fa-calendar-alt"></i>
<input required type="text" name="idade_aluno_lp" id="idade_aluno_lp" placeholder="Digite a idade do aluno" oninput="checkAge()" maxlength="2" />
</div>
<div class="text-center mt-3">
<input type="submit" id="submit2" class="btn btn-primary btn-lg" value="Enviar Resposta" style="display: none;" />
</div>
<div class="text-end mt-3">
<input type="submit" id="previous2" class="btn btn-primary btn-lg" value="Pergunta Anterior" style="display: block;" />
</div>
<script type="text/javascript">
function checkAge() {
var countAge = document.getElementById('idade_aluno_lp');
countAge.value = countAge.value.replace(/\D/, ''); // Remove caracteres que não sejam números
if (countAge.value.length == 0) { // 0 caracteres digitados
document.getElementById('submit2').style.display = 'none';
} else {
document.getElementById('submit2').style.display = 'block';
}
}
</script>
</div>
<div id="question-3">
<h3>Qual seu nome?</h3>
<div class="input-formulario">
<i class="far fa-user"></i>
<input required type="text" name="nome_lp" id="nome_lp" placeholder="Digite seu nome" oninput="checkName()">
<span id="error-name"></span>
</div>
<div class="text-center mt-3">
<input type="submit" id="submit3" class="btn btn-primary btn-lg" value="Enviar Resposta" style="display: none;" />
</div>
<div class="text-center mt-3">
<input type="submit" id="previous3" class="btn btn-primary btn-lg" value="Pergunta Anterior" style="display: block;" />
</div>
<script type="text/javascript">
function checkName() {
var countName = document.getElementById('nome_lp');
var errorName = document.getElementById('error-name')
countName.value = countName.value.replace(/[0-9]/g, ''); // Remove numbers
if (countName.value.length < 3) { // 0 chars entered
errorName.innerHTML = "O nome precisa ter ao menos 3 caracteres."
document.getElementById('submit3').style.display = 'none';
} else {
errorName.innerHTML = "";
document.getElementById('submit3').style.display = 'block';
}
}
</script>
</div>
<div id="question-4">
<h3>Qual seu email?</h3>
<div class="input-formulario">
<i class="fas fa-at"></i>
<input required type="email" name="email_lp" id="email_lp" placeholder="Digite seu email" oninput="checkEmail()">
<span id="error-email"></span>
</div>
<div class="text-center mt-3">
<input type="submit" id="submit4" class="btn btn-primary btn-lg" value="Enviar Resposta" style="display: none;" />
</div>
<div class="text-end mt-3">
<input type="submit" id="previous4" class="btn btn-primary btn-lg" value="Pergunta Anterior" style="display: block;" />
</div>
<script type="text/javascript">
function checkEmail() {
var email = document.getElementById('email_lp');
var errorEmail = document.getElementById('error-email');
if (!email.checkValidity()) {
errorEmail.innerHTML = "O email deverá seguir o padrão: meuemail#provedor.com";
document.getElementById('submit4').style.display = 'none';
} else {
errorEmail.innerHTML = "";
document.getElementById('submit4').style.display = 'block';
}
}
</script>
</div>
<div id="question-5">
<h3>Qual seu whatsapp?</h3>
<div class="input-formulario">
<i class="fab fa-whatsapp"></i>
<input required type="text" name="celular_lp" id="celular_lp" placeholder="Digite seu DDD + celular" maxlength="14" onkeypress="return mascaraCelular(event)" oninput="checkMobile()">
<span id="error-mobile"></span>
</div>
<div class="text-end mt-3">
<input type="submit" id="submit5" class="btn btn-primary btn-lg" value="Enviar Resposta" style="display: none;" />
</div>
<div class="text-end mt-3">
<input type="submit" id="previous5" class="btn btn-primary btn-lg" value="Pergunta Anterior" style="display: block;" />
</div>
</div>
<script type="text/javascript">
function checkMobile() {
var countMobile = document.getElementById('celular_lp');
var errorMobile = document.getElementById('error-mobile');
if (countMobile.value.length < 13) {
errorMobile.innerHTML = "Além do DDD, o número do celular precisa ter ao menos 8 dígitos";
document.getElementById('submit5').style.display = 'none';
} else {
errorMobile.innerHTML = "";
document.getElementById('submit5').style.display = 'block';
}
}
</script>
</form>
And the javascript responsible for show/hide the questions:
// Variables
var submit1 = document.getElementById('submit1');
var submit2 = document.getElementById('submit2');
var submit3 = document.getElementById('submit3');
var submit4 = document.getElementById('submit4');
var previous2 = document.getElementById('previous2');
var previous3 = document.getElementById('previous3');
var previous4 = document.getElementById('previous4');
var previous5 = document.getElementById('previous5');
// Next question button
submit1.addEventListener('click', function() {
nextQuestion(2);
growProgressBar('40%');
})
submit2.addEventListener('click', function() {
nextQuestion(3);
growProgressBar('60%');
})
submit3.addEventListener('click', function() {
nextQuestion(4);
growProgressBar('80%');
})
submit4.addEventListener('click', function() {
nextQuestion(5);
growProgressBar('100%');
})
// grow Progress Bar
function growProgressBar(percentage_width) {
var bar = document.getElementById('progress_bar');
bar.style.width = percentage_width;
}
// Shows next question, hides current
function nextQuestion(question_number) {
var current_question_number = question_number - 1; //question_number is the next question
var question_number = question_number.toString();
var current_question_number = current_question_number.toString();
document.getElementById('question-' + question_number).style.display = 'block';
document.getElementById('question-' + current_question_number).style.display = 'none';
}
// Previous question button
previous2.addEventListener('click', function() {
previousQuestion(1);
shrinkProgressBar('20%');
})
previous3.addEventListener('click', function() {
previousQuestion(2);
shrinkProgressBar('40%');
})
previous4.addEventListener('click', function() {
previousQuestion(3);
shrinkProgressBar('60%');
})
previous5.addEventListener('click', function() {
previousQuestion(4);
shrinkProgressBar('80%');
})
// Shrink Progress Bar
function shrinkProgressBar(percentage_width) {
var bar = document.getElementById('progress_bar');
bar.style.width = percentage_width;
}
// Shows previous question, hides current
function previousQuestion(question_number) {
var current_question_number = question_number + 1;
var question_number = question_number.toString();
var current_question_number = current_question_number.toString();
var el = document.getElementById('question-' + question_number);
console.log(question_number);
var el2 = document.getElementById('question-' + current_question_number);
el.style.display = 'block';
el2.style.display = 'none';
}
About Carsten Massmann answer:
The problem with the code was just to change the buttons type="submit" to type="button", and leave only the last button as type="submit", however, I really liked your code, I learned a lot, for example, I had never read about closest method. Could you explain to me how these two lines of code work (what they are for I understand):
current = (Q.length + current + inc) % Q.length; // cycle through questions...
Above I don't understand why you use the question length.
And on the next line, I don't understand why I use trim():
(el.value.trim() ? "block" : "none")
I also understand that you cycle between questions, but I need to demarcate the first and last question in the loop, and put something like if Q=0, don't show the "Previous question" button, and if Q=4, that the final button would execute method="POST" and action="next-page". I believe that the "submit" is not being executed because of the loop, but I don't understand why the loop takes precedence over the submit.
The following snippet is still missing your actual validation functions for individual fields but I hope it illustrates the point of how you can avoid unnecessary repetitions in your code and markup. I am using classes to address the various input elements and "delegated event attachment" to combine the click handling for different buttons. For simplicity I allow cycling through the questions. Feel free to change and adapt ...
function changeQ(inc){
current= (Q.length+current+inc)%Q.length; // cycle through questions ...
Q.forEach((q,i)=>q.style.display=(i==current?"":"none"))
}
function showHideOK(el){
let ok=!!el.value.trim();
el.closest(".input-formulario").querySelector("input.ok").style.display=
(ok?"block":"none");
ans[el.name]=ok;
}
document.querySelectorAll("div.input-formulario").forEach((div,i)=>{div.innerHTML+=`<div class="text-end mt-3"><input type="submit" class="btn btn-primary btn-lg back" value="Pergunta Anterior"/></div><div class="text-center mt-3"><input type="submit" class="btn btn-primary btn-lg ok" value="Próxima Pergunta"/></div>`;
div.addEventListener("input",ev=>{ let el=ev.target;
if (el.tagName!=="INPUT") return // only act on input elements ...
showHideOK(el)
btn.style.display=Object.values(ans).filter(a=>a).length==Q.length?"":"none";
});
div.onclick=ev=>{if (ev.target.type=="submit") {
ev.preventDefault();
changeQ( ev.target.classList.contains("back")?-1:1 );
}}
})
const btn = document.querySelector("button"),
Q=document.querySelectorAll(".question"),
ans={};
let current=0;
changeQ(0);
btn.style.display="none";
input.ok {display:none}
<form class="questions_box formulario" action="enviar-landing-page-v3.php" method="post">
<div class="question">
<div class="input-formulario">
<h3>The question is: ... ?</h3>
<label><input type="radio" name="objetivo" value=1 required> Answer 1.</label><br>
<label><input type="radio" name="objetivo" value=2> Answer 2.</label><br>
<label><input type="radio" name="objetivo" value=3> Answer 3.</label><br>
</div>
</div>
<div class="question">
<h3>Quantos anos tem seu filho(a)?</h3>
<div class="input-formulario">
<input required type="number" name="idade_aluno_lp" placeholder="Digite a idade do aluno" maxlength="2" />
</div>
</div>
<div class="question">
<h3>Qual seu nome?</h3>
<div class="input-formulario">
<input required type="text" name="nome_lp" placeholder="Digite seu nome"> <span></span>
</div>
</div>
<div class="question">
<h3>Qual seu email?</h3>
<div class="input-formulario">
<input required type="email" name="email_lp" id="email_lp" placeholder="Digite seu email"> <span></span>
</div>
</div>
<div class="question">
<h3>Qual seu whatsapp?</h3>
<div class="input-formulario">
<input required type="text" name="celular_lp" id="celular_lp" placeholder="Digite seu DDD + celular"
maxlength="14"> <span></span>
</div>
</div>
<br><button>Enviar todas as respostas</button>
</form>
Further explanations:
Why am I using Q.length?
The expression current = (Q.length + current + inc)/Q.length uses Q.length (=total number of questions) to keep current always within the range of existing question indexes. The term Q length+... is necessary to keep values positive for the situation where current=0 and inc=-1 and % Q.length limits the results from 0 to Q.length-1.
Why do I use .trim()?
This is just a simple way of avoiding that an answer containing only blanks would be accepted as a valid answer.
Why is there no "submit"?
I actively disabled the submit action by calling ev.preventDefault() within my event-handling function.
How to submit the answers, and when?
There are many different ways of doing it. My suggestion would be to keep cycling through the questions until all questions are answered in a valid way. At that point an extra button appears that submits all answers at once. I just added that button (btn) to the script. It is hidden at first through btn.style.display="none" but made visible again on the condition that all questions have received an answer:
btn.style.display=Object.values(ans).filter(a=>a).length==Q.length?"":"none";
The (global) object ans is constantly updated in the function showHideOK():
let ok=!!el.value.trim();
...
ans[el.name]=ok;
I have been refactoring some of my innerHTML content within a form to make it more readable. Before, I had one lump innerHTML which was hard to read, however worked and allowed updating to localStorage.
I gave each div wrapper its own unique ID so that I could change the innerHTML value, in order to make everything neater and readable, this worked fine but when I try to edit my form now it gives me an error of cannot set property 'fname' of undefined. When I revert it back to the lump html it edits fine?
My knowledge is limited of how JS works, so can anyone explain why this might be happening, and is there a resolution?
Here's my code and edit handler (simplified), it's difficult to show on this as it's using localStorage but hopefully it's at least helpful:
$(document).on('change keyup', '.required', function(e) {
var disabled = true;
$(".required").each(function() {
var value = this.value;
if (!(value) || (value.trim() === '') || ($(this).hasClass('is-invalid'))) {
disabled = false;
$('.toggle-disabled').prop("disabled", true);
}
});
if (disabled) {
$('.toggle-disabled').prop("disabled", false);
}
});
var bookings = JSON.parse(localStorage.getItem("bookings")) || [];
$("#submit").click(function() {
var newBookings = {
id: new Date().getTime(),
fname: $('#fname').val(),
lname: $('#lname').val(),
}
bookings.push(newBookings);
var json = JSON.stringify(bookings);
window.localStorage.setItem("bookings", json);
showBooking();
document.getElementById('formSuccess').style.display = "block";
});
$(document).on('click', '#edit', function(e) {
e.preventDefault();
var parent_form = $('this.form');
var fname = parent_form.find('.fname').val();
var lname = parent_form.find('.lname').val();
let i = bookings.findIndex(booking => booking.id == $(this).data("id"));
bookings[i].fname = fname;
bookings[i].lname = lname;
var json = JSON.stringify(bookings);
window.localStorage.setItem("bookings", json);
alert('Form updated!');
window.location.reload();
showBooking();
});
function showBooking() {
var bookingResult = document.getElementById("result");
var ul = document.createElement("ul");
bookingResult.innerHTML = `<h3 class="text-center">Your Bookings</h3>`;
for (let i = 0; i < bookings.length; i++) {
bookingResult.innerHTML += `
<div class="card card-body bg-light m-4">
<div class="row">
<p>Owner name: ${bookings[i].fname + " " + bookings[i].lname}</p>
</div>
<div class="row">
<div class="d-grid gap-2 d-md-block">
<button onclick="editBooking(${i})" class="col-md-4 btn btn-outline-danger ">Edit</button>
<button onclick="deleteBooking(${i})" class="col-md-4 btn btn-danger text-light ">Delete</button>
</div>
</div>
</div>`;
}
}
function editBooking(i) {
$('#result').hide();
var fnameEdit = document.getElementById("fnameEdit");
var lnameEdit = document.getElementById("lnameEdit");
var editButton = document.getElementById("editBtn");
fnameEdit.innerHTML = `<input type="text" class="fname form-control required" data-id="${bookings[i].id}" placeholder="First Name" name="${bookings[i].fname}" value="${bookings[i].fname}" required>`;
lnameEdit.innerHTML = `
<input type="text" class="lname form-control required" data-id="${bookings[i].id}" placeholder="Last Name" name="${bookings[i].lname}" value="${bookings[i].lname}" required>`;
editButton.innerHTML = `<div class="d-grid gap-2 d-md-block">
<input id="edit" class="btn btn-danger toggle-disabled" type="submit" value="Edit">
Cancel
</div>`;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<form id="regForm" name="regForm" action="" class="col-md-6">
<div class="row">
<div id="fnameEdit" class="col-md-6">
<input type="text" class="input form-control required" id="fname" placeholder="First Name" name="fname" required>
</div>
<div id="lnameEdit" class="col-md-6">
<input type="text" class="input form-control required" id="lname" placeholder="Last Name" name="lname" required>
</div>
</div>
<div id="editBtn">
<div class="col-md-6">
<input id="submit" class="btn btn-danger toggle-disabled" type="submit" value="Submit">
</div>
</div>
</form>
<div class="col-md-6">
<div id="result" class="row"></div>
<div id="currentItem" class="row"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
The first issue is described in the comment by #Swati (i.e.: $(this).closest('form'))
As a consequence, the second issue is related to the anonymous function you use inside .findIndex():
Change this part from:
let i = bookings.findIndex(booking => booking.id == $(this).data("id"));
to:
let i = bookings.findIndex(booking => booking.id == parent_form.find('.fname').data("id"));
$(document).on('change keyup', '.required', function(e) {
var disabled = true;
$(".required").each(function() {
var value = this.value;
if (!(value) || (value.trim() === '') || ($(this).hasClass('is-invalid'))) {
disabled = false;
$('.toggle-disabled').prop("disabled", true);
}
});
if (disabled) {
$('.toggle-disabled').prop("disabled", false);
}
});
var bookings = JSON.parse(localStorage.getItem("bookings")) || [];
$("#submit").click(function() {
var newBookings = {
id: new Date().getTime(),
fname: $('#fname').val(),
lname: $('#lname').val(),
}
bookings.push(newBookings);
var json = JSON.stringify(bookings);
window.localStorage.setItem("bookings", json);
showBooking();
document.getElementById('formSuccess').style.display = "block";
});
$(document).on('click', '#edit', function(e) {
e.preventDefault();
var parent_form = $(this).closest('form');
var fname = parent_form.find('.fname').val();
var lname = parent_form.find('.lname').val();
let i = bookings.findIndex(booking => booking.id == parent_form.find('.fname').data("id"));
bookings[i].fname = fname;
bookings[i].lname = lname;
var json = JSON.stringify(bookings);
window.localStorage.setItem("bookings", json);
alert('Form updated!');
window.location.reload();
showBooking();
});
function showBooking() {
var bookingResult = document.getElementById("result");
var ul = document.createElement("ul");
bookingResult.innerHTML = `<h3 class="text-center">Your Bookings</h3>`;
for (let i = 0; i < bookings.length; i++) {
bookingResult.innerHTML += `
<div class="card card-body bg-light m-4">
<div class="row">
<p>Owner name: ${bookings[i].fname + " " + bookings[i].lname}</p>
</div>
<div class="row">
<div class="d-grid gap-2 d-md-block">
<button onclick="editBooking(${i})" class="col-md-4 btn btn-outline-danger ">Edit</button>
<button onclick="deleteBooking(${i})" class="col-md-4 btn btn-danger text-light ">Delete</button>
</div>
</div>
</div>`;
}
}
function editBooking(i) {
$('#result').hide();
var fnameEdit = document.getElementById("fnameEdit");
var lnameEdit = document.getElementById("lnameEdit");
var editButton = document.getElementById("editBtn");
fnameEdit.innerHTML = `<input type="text" class="fname form-control required" data-id="${bookings[i].id}" placeholder="First Name" name="${bookings[i].fname}" value="${bookings[i].fname}" required>`;
lnameEdit.innerHTML = `
<input type="text" class="lname form-control required" data-id="${bookings[i].id}" placeholder="Last Name" name="${bookings[i].lname}" value="${bookings[i].lname}" required>`;
editButton.innerHTML = `<div class="d-grid gap-2 d-md-block">
<input id="edit" class="btn btn-danger toggle-disabled" type="submit" value="Edit">
Cancel
</div>`;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.bundle.min.js"></script>
<form id="regForm" name="regForm" action="" class="col-md-6">
<div class="row">
<div id="fnameEdit" class="col-md-6">
<input type="text" class="input form-control required" id="fname" placeholder="First Name" name="fname" required>
</div>
<div id="lnameEdit" class="col-md-6">
<input type="text" class="input form-control required" id="lname" placeholder="Last Name" name="lname" required>
</div>
</div>
<div id="editBtn">
<div class="col-md-6">
<input id="submit" class="btn btn-danger toggle-disabled" type="submit" value="Submit">
</div>
</div>
</form>
<div class="col-md-6">
<div id="result" class="row"></div>
<div id="currentItem" class="row"></div>
</div>
I ended up solving this problem eventually, I hadn't given the attributes a new ID and somehow, that sorted the issue. I removed the innerHTML and replaced with setAttribute methods which work fine.
Thanks for help!
Something along these lines:
var fname = document.getElementById("fname");
var lname = document.getElementById("lname");
var editButton = document.getElementById("editBtn");
fname.setAttribute('data-id', bookings[i].id);
fname.setAttribute('value', bookings[i].fname);
lname.setAttribute('data-id', bookings[i].id);
lname.setAttribute('value', bookings[i].lname);
editButton.innerHTML = `<div class="d-grid gap-2 d-md-block">
<input data-id="${bookings[i].id}" id="edit" class="btn btn-danger toggle-disabled" type="submit" value="Edit" disabled>
Cancel
</div>`;
I am creating a HTML form for data entry that contains a couple of textboxes and function buttons.
There is also an add button that copies (clones) from a DIV template (id: w) for each "row" and appends to the end of the main DIV (id: t). On each row, there is a "X" button to remove the row and two arrow buttons to visually move the "row" up and down in the DOM.
The form is dynamically created from a database and the elements already on the page when the page is loaded and using jQuery 3.4.1, for the selecting and most of the DOM manipulation using the functionality of each rows buttons.
The "rows" are added to the container DIV and the elements are renamed depending on the counter which is expected. The "X" button deletes the "row", and moves all pre-existing rows up and down in the container DIV.
But for some unknown reason any new rows that are created I have to press the "up" button twice. The "down" button for the bottom row, is redundant and not functional.
I think it might have to do with the previousSibling and nextSibling returning the wrong Object type and causing a problem and failing the first time.
Any thoughts on how to fix or improve this functionality?
var rr = $("[id^=l]").length;
$(".data-up").click(function() {
var e = $(this).first().parent().parent().get(0);
moveUp(e);
});
$(".data-down").click(function() {
var e = $(this).parent().parent().get(0);
moveDown(e);
});
$(".remove").click(function() {
$(this).parent().parent().remove();
});
function add() {
rr += 1;
var a = $("#w").clone(true, true).removeAttr('id').removeAttr('style');
a.attr("datarow", "row" + rw);
a.find("input[data-field='l']").attr("id", "l" + rr).attr("name", "l" + rr).removeAttr("data-field");
a.find("input[data-field='s']").attr("id", "s" + rr).attr("name", "s" + rr).removeAttr("data-field");
a.appendTo("#t");
}
function moveUp(e) {
if (e.previousSibling) {
if (e.previousSibling === e.parentNode.children[0]) {} else {
e.parentNode.insertBefore(e, e.previousSibling);
}
}
}
function moveDown(e) {
if (e === e.parentNode.children[e.parentNode.children.length - 1]) {} else {
e.parentNode.insertBefore(e.nextSibling, e);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<button type="button" class="btn btn-secondary" ID="p" onclick="add();">Add</button>
<div id="t">
</div>
<div class="form-group row" id="w" style="display:none;" datarow="row">
<div class="col-sm-1"><input name="s" class="form-control txt-s col-form-label" id="s" type="text" readonly="true" data-field="s"></div>
<div class="col-sm-3"><input name="l" class="form-control txt-l" id="l" type="text" data-field="l"></div>
<div class="col-sm-2">
<button class="btn btn-danger mr-1 remove" type="button">X</button>
<button class="btn btn-info mr-1 data-up" type="button">↑</button>
<button class="btn btn-info data-down" type="button">↓</button>
</div>
</div>
With jQuery - use .closest() to find the current parent of the button. The find the .prev() or the .next() sibling. If the sibling exists use .insertBefore() or .insertAfter() to move the current parent before or after the sibling:
var rr = $("[id^=l]").length;
$(".data-up").click(function(e) {
var current = $(this).closest('.form-group'); // find the current parent
var target = current.prev(); // find the relevant sibling
if(target.length) { // if sibling exists
current.insertBefore(target); // insert the current item above it
}
});
$(".data-down").click(function() {
var current = $(this).closest('.form-group'); // find the current parent
var target = current.next(); // find the next sibling
if(target.length) { // if the next sibling exists
current.insertAfter(target); // insert the current item after it
}
});
$(".remove").click(function() {
$(this).parent().parent().remove();
});
function add() {
rr += 1;
var a = $("#w").clone(true, true).removeAttr('id').removeAttr('style');
a.attr("datarow", "row" + rr);
a.find("input[data-field='l']").attr("id", "l" + rr).attr("name", "l" + rr).removeAttr("data-field");
a.find("input[data-field='s']").attr("id", "s" + rr).attr("name", "s" + rr).removeAttr("data-field");
a.appendTo("#t");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<button type="button" class="btn btn-secondary" ID="p" onclick="add();">Add</button>
<div id="t">
</div>
<div class="form-group row" id="w" style="display:none;" datarow="row">
<div class="col-sm-1"><input name="s" class="form-control txt-s col-form-label" id="s" type="text" readonly="true" data-field="s"></div>
<div class="col-sm-3"><input name="l" class="form-control txt-l" id="l" type="text" data-field="l"></div>
<div class="col-sm-2">
<button class="btn btn-danger mr-1 remove" type="button">X</button>
<button class="btn btn-info mr-1 data-up" type="button">↑</button>
<button class="btn btn-info data-down" type="button">↓</button>
</div>
</div>
What's wrong in your code
The problem with the up button is e.previousSibling === e.parentNode.children[0] - the 1st element is always the first item in the collection, so this blocks you from moving an item above it. All you have to check is if there is a previousSibling for up, and nextSibling for down.
Fixed code + comments:
var rr = $("[id^=l]").length;
$(".data-up").click(function() {
var e = $(this).parent().parent().get(0); // first() is redundant - this is the only element in the collection
moveUp(e);
});
$(".data-down").click(function() {
var e = $(this).parent().parent().get(0);
moveDown(e);
});
$(".remove").click(function() {
$(this).parent().parent().remove();
});
function add() {
rr += 1;
var a = $("#w").clone(true, true).removeAttr('id').removeAttr('style');
a.attr("datarow", "row" + rr);
a.find("input[data-field='l']").attr("id", "l" + rr).attr("name", "l" + rr).removeAttr("data-field");
a.find("input[data-field='s']").attr("id", "s" + rr).attr("name", "s" + rr).removeAttr("data-field");
a.appendTo("#t");
}
function moveUp(e) {
if (e.previousSibling) { // just check if there's a previous sibling
e.parentNode.insertBefore(e, e.previousSibling);
}
}
function moveDown(e) {
if (e.nextSibling) { // just check if there's a next sibling
e.parentNode.insertBefore(e.nextSibling, e);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<button type="button" class="btn btn-secondary" ID="p" onclick="add();">Add</button>
<div id="t">
</div>
<div class="form-group row" id="w" style="display:none;" datarow="row">
<div class="col-sm-1"><input name="s" class="form-control txt-s col-form-label" id="s" type="text" readonly="true" data-field="s"></div>
<div class="col-sm-3"><input name="l" class="form-control txt-l" id="l" type="text" data-field="l"></div>
<div class="col-sm-2">
<button class="btn btn-danger mr-1 remove" type="button">X</button>
<button class="btn btn-info mr-1 data-up" type="button">↑</button>
<button class="btn btn-info data-down" type="button">↓</button>
</div>
</div>
I am implementing an extend form function, in which there is a label text (marked in html) that I hope to include a number to increment.
So every time when a form is extended/cloned, the label text in the extended form shows Student 1, Student 2... accordingly. Can I be advised how to do that?
var counter = 0;
function moreFields(val1, val2, val3) {
counter++;
var newField = document.getElementById(val1).cloneNode(true);
newField.id = '';
newField.style.display = 'block';
var newFields = newField.querySelectorAll('[name], [id], [for]');
for (var i=0;i<newFields.length;i++) {
var theNames = newFields[i].name
if (theNames)
newFields[i].name = "data[" + val3 + "][" + counter + "][" + theNames + "]";
var theNames2 = newFields[i].id;
if (theNames2)
newFields[i].id = theNames2 + counter;
var theNames3 = newFields[i].htmlFor;
if (theNames3)
newFields[i].htmlFor = theNames3 + counter;
}
var insertHere = document.getElementById(val2);
insertHere.parentNode.insertBefore(newField,insertHere);
}
<span id="readroot" style="display: none">
<div class="row">
<div class="col-lg-3">
Grade 6
</div>
<div class="col-lg-3">
Male
</div>
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12">
<!-- Student number needs to increase when a new form is extended. -->
<label>Student 1</label>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<label for="display_student_1_grade">Grade</label>
<div id="display_student_1_grade"></div>
</div>
<div class="col-lg-6">
<label for="display_student_1_gender">Gender</label>
<div id="display_student_1_gender"></div>
</div>
</div>
</div>
</div>
<input class="btn btn-default" type="button" value="Remove" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" />
</span>
<span id="writeroot"></span>
<input class="btn btn-default" type="button" onclick="moreFields('readroot', 'writeroot', 'Student')" value="One More Student" />
To answer the question directly, you just need to find the appropriate <label> and update the innerHTML (see below).
However, what you are doing here can be achieved using the new HTML5 template element without having to hide <span> elements. Additionally, you have to remember that when you remove a student, the counter isn't going to decrease, nor are the already added students going to update. If you want that kind of functionality, you may want to look into a Javascript MVVM, like Angular.
var counter = 0;
function moreFields(val1, val2, val3) {
counter++;
var newField = document.getElementById(val1).cloneNode(true);
newField.id = '';
newField.style.display = 'block';
// Find the label here and update the innerHTML appropriately
newField.querySelector(".col-lg-12 label").innerHTML = "Student " + counter;
var newFields = newField.querySelectorAll('[name], [id], [for]');
for (var i=0;i<newFields.length;i++) {
var theNames = newFields[i].name
if (theNames)
newFields[i].name = "data[" + val3 + "][" + counter + "][" + theNames + "]";
var theNames2 = newFields[i].id;
if (theNames2)
newFields[i].id = theNames2 + counter;
var theNames3 = newFields[i].htmlFor;
if (theNames3)
newFields[i].htmlFor = theNames3 + counter;
}
var insertHere = document.getElementById(val2);
insertHere.parentNode.insertBefore(newField,insertHere);
}
<span id="readroot" style="display: none">
<div class="row">
<div class="col-lg-3">
Grade 6
</div>
<div class="col-lg-3">
Male
</div>
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12">
<!-- Student number needs to increase when a new form is extended. -->
<label>Student 1</label>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<label for="display_student_1_grade">Grade</label>
<div id="display_student_1_grade"></div>
</div>
<div class="col-lg-6">
<label for="display_student_1_gender">Gender</label>
<div id="display_student_1_gender"></div>
</div>
</div>
</div>
</div>
<input class="btn btn-default" type="button" value="Remove" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" />
</span>
<span id="writeroot"></span>
<input class="btn btn-default" type="button" onclick="moreFields('readroot', 'writeroot', 'Student')" value="One More Student" />