I am trying to rewrite this Jquery http://jsfiddle.net/Claudius/gDChA/4/ to use this HTML instead http://pastie.org/2370829
Jquery:
$('button.add', '#companyinfo').live('click', function(e) {
$(this).parent().find('button.remove').show();
$(this).hide();
var element = $(this).parents('.input').find('input').last().clone().prop('value','');
var name = element.prop('name');
var pattern = new RegExp(/\[(.*?)\]/);
var info = name.match(pattern)[1];
var newname = name.replace(pattern, '[' + info + 'info' + ']');
var newid = element.prop('id') + 'info';
element.prop('name', newname);
element.prop('id', newid);
element.insertAfter($(this).parents('.input').find('input').last());
})
$('button.remove', '#companyinfo').live('click', function(e) {
$(this).parent().find('button.add').show();
$(this).hide();
$(this).parents('.input').find('input').last().remove('input');
});
Old HTML
<fieldset id='companyinfo'><legend>Company info</legend>
<div class='input string optional'>
<label for='company_navn' class='string optional'>Count</label>
<input type='text' size='50' name='company[count]' id='company_navn' maxlength="255" class='string optional' />
<div class='button-row'>
<button class='add'>Add info</button>
<button class='remove'>Remove</button>
</div>
</div>
<div class="input string optional">
<label for="company_navn" class="string optional">Navn</label>
<input type="text" size="50" name="company[navn]" maxlength="255" id="company_navn" class="string optional">
<div class='button-row'>
<button class='add'>Add info</button>
<button class='remove'>Remove</button>
</div>
</div>
</fieldset>
New HTML:
<div class="input string optional"><label for="virksomhed_navn" class="string optional"> Navn</label><input type="text" size="50" name="virksomhed[navn]" maxlength="255" id="virksomhed_navn" class="string optional"></div>
<div class="button-row" style="font-size: 11px; width: 110px; float: right; margin-top: -10px; margin-right: 16px;">
<button class="add" style="font-size: 11px;">Add info</button>
<button class="remove" style="font-size: 11px;">Remove</button>
</div>
<div class="input string optional"><label for="virksomhed_name" class="string optional">Name</label><input type="text" size="50" name="virksomhed[name]" maxlength="255" id="virksomhed_name" class="string optional"></div>
<div class="button-row" style="font-size: 11px; width: 110px; float: right; margin-top: -10px; margin-right: 16px;">
<button class="add" style="font-size: 11px;">Add info</button>
<button class="remove" style="font-size: 11px;">Remove</button>
</div>
<div class="input string optional"><label for="virksomhed_pis" class="string optional">Pis</label><input type="text" size="50" name="virksomhed[v]" maxlength="255" id="virksomhed_pis" class="string optional"></div>
<div class="button-row" style="font-size: 11px; width: 110px; float: right; margin-top: -10px; margin-right: 16px;">
<button class="add" style="font-size: 11px;">Add info</button>
<button class="remove" style="font-size: 11px;">Remove</button>
</div>
How can I select the inputs fields in the new HTML and create the same functionality ?
There are two places you need to change.
The buttons that the handlers hook on to.
The way you get the last input field.
And there are some minor enhancements you can do:
Since the buttons are static, you can use click() to add event handler instead of live()
At three place you need to find the last input field relative to current button; making it a function would make it easier to understand and modify.
most jQuery set functions, including prop(), can take an object to set multiple values. This is usually better then calling the same function many times.
So, the final result:
function findLastInput ( element ) {
return $( element ).parent().prev().find('input').last();
}
$('button.add').click ( function(e) {
$(this).parent().find('button.remove').show();
$(this).hide();
var element = findLastInput(this).clone();
var name = element.prop('name');
var pattern = new RegExp(/\[(.*?)\]/);
var info = name.match(pattern)[1];
element.prop({
'value': '',
'name' : name.replace(pattern, '[' + info + 'info' + ']'),
'id' : element.prop('id') + 'info'
});
element.insertAfter(findLastInput(this));
})
$('button.remove').click ( function(e) {
$(this).parent().find('button.add').show();
$(this).hide();
findLastInput(this).remove('input');
});
Related
i would like help for some improvement i would like to change the structure of my html this is the image below
I would like that when user press the plus a new input box however the right side there is a minus instead and if press minus the input box be gone
Example of image below
currently what i have is on my code is just like this
So how can i change it to be like the example of the image?
$('.add').on('click', add);
$('.remove').on('click', remove);
function add() {
var new_chq_no = parseInt($('#total_chq').val()) + 1;
var new_input = "<div style='margin-bottom:5px;'><input type='text' id='new_" + new_chq_no + "'pattern='^[0-9]{8}$' class='form-control col-9' required><div class='invalid-feedback'>Enter a correct PhoneNumber!</div></div>";
$('#new_chq').append(new_input);
$('#total_chq').val(new_chq_no);
}
function remove() {
var last_chq_no = $('#total_chq').val();
if (last_chq_no > 1) {
$('#new_' + last_chq_no).remove();
$('#total_chq').val(last_chq_no - 1);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="form-group row">
<label for="validationNumber" class="col-2 col-form-label">Contact:</label>
<div class="col-4">
<div class="flex" style="margin-bottom:5px;">
<input style="margin-right: 19px;" id="validationNumber" name="phonenumber" type="text" class="form-control" pattern="\b\d{8}\b" required>
<a onclick="add()"><label style="cursor: pointer; padding-top: 5px;"><i data-feather="plus">+</i></label></a>
<a onclick="remove()"><label style="cursor: pointer; padding-top: 5px;"><i data-feather="minus">-</i></label></a>
</div>
<div id="new_chq"> </div>
<input type="hidden" value="1" id="total_chq">
<div class="invalid-feedback">
Enter a correct PhoneNumber!
</div>
</div>
</div>
Here is a version that is using clone and delegation
Wrap the set in a container of their own
Wrap the containers in a container
Check that the user cannot delete the last row
Clone the number including the possible error message
No need for ID, you can navigate using closest and the name
const $container = $('#contactContainer')
$(".remove").eq(0).hide()
$container.on('click', ".ar", function(e) {
const add = $(this).is(".add");
const $phones = $container.find(".phone");
const len = $phones.length;
if (add) {
const $newPhone = $phones.eq(0).clone(true)
$newPhone.find("[name=phonenumber]")
.attr("id", `new_${$phones.length}`)
.val("");
$container.append($newPhone);
$newPhone.find(".add").hide(); // if you only want one plus
$newPhone.find(".remove").show()
} else {
$(this).closest(".phone").remove()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="form-group row">
<label class="col-2 col-form-label">Contact:</label>
<div class="col-4" id="contactContainer">
<div class="flex phone" style="margin-bottom:5px;">
<input style="margin-right: 19px;" name="phonenumber" type="text" class="form-control" pattern="\b\d{8}\b" required>
<span class="ar add"><label style="cursor: pointer; padding-top: 5px;"><i data-feather="plus">+</i></label></span>
<span class="ar remove"><label style="cursor: pointer; padding-top: 5px;"><i data-feather="minus">-</i></label></span>
<div class="invalid-feedback">
Enter a correct PhoneNumber!
</div>
</div>
</div>
</div>
I am trying to get the text in a multiple text box as the user types in it (jsfiddle playground):
function Input1(a) {
document.getElementById("Input11").innerHTML = a.value;
document.getElementById("Input12").innerHTML = a.value;
document.getElementById("Input13").innerHTML = a.value;
}
function Input2(b) {
document.getElementById("Input21").innerHTML = b.value;
document.getElementById("Input22").innerHTML = b.value;
document.getElementById("Input23").innerHTML = b.value;
}
And Result as
<span id="text-box">
<input class="textbox-value" type="text" name="Jname" placeholder="Input1" onkeyup="Input1(this);">
</span><br><br>
<span id="Input11">Input11</span><br>
<span id="Input12">Input12</span><br>
<span id="Input13">Input13</span><br><br>
<span id="text-box">
<input class="textbox-value" type="text" name="Jbname" placeholder="Input2" onkeyup="Input2(this);">
</span><br><br>
<span id="Input21">Input21</span><br>
<span id="Input22">Input22</span><br>
<span id="Input23">Input23</span><br><br>
The above code is working fine.
But I want to Display each "onkeyup" input multiple times on-page. So here I need to update the function and span id (As if I use the same id then it will not display anything after 2nd call)
Please help me to reformat the above JavaScript and HTML so Just Define function for input and display it on all HTML span id without changing span id each time...
you can use class instead of id and use querySelectorAll to select all elements here is sample code
function Input1(a) {
const elements = document.querySelectorAll(".Input1");
elements.forEach((e)=>{
e.innerHTML = a.value;
})
}
function Input2(b) {
const elements = document.querySelectorAll(".Input2");
elements.forEach((e)=>{
e.innerHTML = b.value;
})
}
<html>
<body><span id="text-box">
<input class="textbox-value" type="text" name="Jname" placeholder="Input1" onkeyup="Input1(this);">
</span><br><br>
<span class="Input1">Input11</span><br>
<span class="Input1">Input12</span><br>
<span class="Input1">Input13</span><br><br>
<span id="text-box">
<input class="textbox-value" type="text" name="Jbname" placeholder="Input2" onkeyup="Input2(this);">
</span><br><br>
<span class="Input2">Input21</span><br>
<span class="Input2">Input22</span><br>
<span class="Input2">Input23</span><br><br>
</body>
</html>
you can do somthing like that:
document.querySelectorAll('div.text-box').forEach( (box,i) =>
{
let
intxt = box.querySelector('input')
, spTxts = box.querySelectorAll('span')
;
intxt.mane = `Jname${++i}`
intxt.placeholder = `Input${i}`
intxt.onkeyup = () => spTxts.forEach(sp=>sp.textContent = intxt.value)
})
.text-box {
margin : 20px 0 15px 0;
}
.text-box input {
width : 100%;
font-size : 13px;
padding : 5px;
margin-top : -5px;
margin-bottom : 1em;
box-shadow : 1px 5px 7px #75757578;
}
.text-box span {
display : block;
}
<div class="text-box">
<input type="text">
<span>...</span>
<span>...</span>
<span>...</span>
</div>
<div class="text-box">
<input type="text">
<span>...</span>
<span>...</span>
<span>...</span>
</div>
<div class="text-box">
<input type="text">
<span>...</span>
<span>...</span>
<span>...</span>
</div>
<div class="text-box">
<input type="text">
<span>...</span>
<span>...</span>
<span>...</span>
</div>
this is the best for you!
function Input(a, n) {
var spans = document.querySelectorAll('#tb' + n + ' span')
spans.forEach(function(span){
span.innerHTML = a.value;
})
}
.textbox-value {
width: 100%;
font-size: 13px;
padding: 5px;
margin-top: -5px;
box-shadow: 1px 5px 7px #75757578;
}
<html>
<body>
<div id="tb1" class="text-box">
<input class="textbox-value" type="text" name="Jname" placeholder="Input1" onkeyup="Input(this, '1');" />
<br><br>
<span>Input11</span><br>
<span>Input12</span><br>
<span>Input13</span><br>
</div>
<br>
<div id="tb2" class="text-box">
<input class="textbox-value" type="text" name="Jbname" placeholder="Input2" onkeyup="Input(this, '2');">
<br><br>
<span>Input21</span><br>
<span>Input22</span><br>
<span>Input23</span><br>
</div>
<br>
</body>
</html>
Based on a previous question of mine here i have three input fields with the same css class price-input and i want after clicking on the button with the id set-price-btn to fill them with the input value displayed at the bottom of each input field using pure JavaScript.
The problem is that everytime i click the button, the variable videosize returns undefined. What I am doing wrong ?
var setpricebtn = document.getElementById("set-price-btn");
setpricebtn.addEventListener("click", () => {
var priceinputs = document.querySelectorAll("price-input");
var videosize = document.querySelectorAll("video-file-size").value;
for (var i = 0; i < videosize.value; i++) {
var savedprice = videosize[i].value;
priceinputs[i].value = savedprice;
}
});
.toolbar {
width:100%;
overflow:hidden;
margin-bottom:30px;
}
.btn {
width:auto;
display:inline-block;
padding:10px;
text-align:center;
background:#e8e8e8;
cursor:pointer;
border-radius:4px;
font-weight:bold;
font-size:12px;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap#4.6.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
<div class="toolbar">
<div class="btn btn-primary" id="set-price-btn"> Set price to all fields </div>
</div>
<div class="row">
<div class="col-3 item">
<input name="price" placeholder="Enter price" class="price-input" value="" min="0" type="number">
<input name="uf" class="video-file-size" value="155" type="text">
</div>
<div class="col-3 item">
<input name="price" placeholder="Enter price" class="price-input" value="" min="0" type="number">
<input name="uf" class="video-file-size" value="185" type="text">
</div>
<div class="col-3 item">
<input name="price" placeholder="Enter price" class="price-input" value="" min="0" type="number">
<input name="uf" class="video-file-size" value="314" type="text">
</div>
</div>
Few things to remember:
This is how you query based on classes when using querySelector. You have to use a ..
videosize will be a live collection of HTML Nodes.
var priceinputs = document.querySelectorAll(".price-input");
var videosize = document.querySelectorAll(".video-file-size");
for (var i = 0; i < videosize.length; i++) {
var savedprice = videosize[i].value;
priceinputs[i].value = savedprice;
}
});
You cannot do var videosize = document.querySelectorAll("video-file-size").value because document.querySelectorAll("video-file-size") returns a NodeList. See docs.
And either you use document.querySelectorAll(".video-file-size") or document.getElementsByClassName('video-file-size').
I have a form in my HTML that takes in first name, last name, and phone number to create an account ID. The input textboxes for first name, last name, and account ID accept keyboard input and display it, as would be expected. However, when I'm viewing the page on the Firefox browser, only the phone number textbox doesn't work. I can click into the box once and see the cursor, but as soon as I start typing, no text shows up, and the cursor disappears. However, based on the Javascript creating an account ID with the last four digits of the phone number typed, I know the input is recognized. It works in other browsers, just not in Firefox.
<article>
<h2>New Account Information</h2>
<form>
<fieldset id="deliveryinfo">
<label for="fnameinputacct">First Name</label>
<input type="text" id="fnameinputacct" name="fname" />
<label for="lnameinputacct">Last Name</label>
<input type="text" id="lnameinputacct" name="lname" />
<label for="phoneinputacct">Phone Number</label>
<input type="text" id="phoneinputacct" name="phone" />
<label for="accountidbox">Account ID</label>
<input type="text" id="accountidbox" name="accountid" />
</fieldset>
<fieldset id="submitbutton">
<input type="submit" id="submitBtn" value="Create Account" />
</fieldset>
</form>
</article>
Here is the CSS
fieldset {
margin-bottom: 10px;
position: relative;
padding: 2.5em 1em 0.5em 1em;
background: #e3d5ba;
}
#deliveryinfo label {
display: block;
float: left;
clear: left;
margin-top: 5px;
}
#deliveryinfo input {
display: block;
margin-left: 130px;
}
#fnameinputacct, #lnameinputacct, #phoneinputacct, #accountidbox {
width: 12em;
}
#submitBtn {
font-size: 1.25em;
}
And some Javascript that goes with the fields. This method is added in another function.
function createID() {
var fname = document.getElementById("fnameinputacct");
var lname = document.getElementById("lnameinputacct");
var phone = document.getElementById("phoneinputacct");
var account = document.getElementById("accountidbox");
var fields = document.getElementsByTagName("input");
var acctid;
var fistInit;
var lastInit;
if (fname != "" && lname != "" && phone != "") {
fistInit = fname.value.charAt(0).toUpperCase();
lastInit = lname.value.charAt(0).toUpperCase();
acctid = fistInit + lastInit + phone.value.substring(phone.value.length - 4);
account.value = acctid;
newAccountArray = [];
for (var i = 0; i < fields.length - 1; i++) {
newAccountArray.push(fields[i].value);
}
}
}
You might try breaking up your form field groups with a <div> or <p>.
See https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/How_to_structure_an_HTML_form
Some of the widely used css frameworks do this as well. Look at Semantic UI, Bootstrap, or Material. These do a similar grouping with div containers for each label/input
Example from semantic ui form:
<form class="ui form">
<div class="field">
<label>First Name</label>
<input type="text" name="first-name" placeholder="First Name">
</div>
<div class="field">
<label>Last Name</label>
<input type="text" name="last-name" placeholder="Last Name">
</div>
<div class="field">
<div class="ui checkbox">
<input type="checkbox" tabindex="0" class="hidden">
<label>I agree to the Terms and Conditions</label>
</div>
</div>
<button class="ui button" type="submit">Submit</button>
</form>
Creating Dynamic new fields seems to clear the already existing fields
Also not trying to have multiple elements with the same id hence why i don't believe appendChild will work. Perhaps you can find a way to do that while creating different IDs?
Any help welcomed =)
var template;
var a = 1;
window.onload = function() {
template = document.querySelector("#wrapper").innerHTML;
document.querySelector("#more_fields").addEventListener("click", function(e) {
e.preventDefault(); // tell the browser to not send the form
document.getElementById('wrapper').innerHTML += template; // add next segment
document.querySelector("#wrapper > label:last-of-type").innerHTML = "Segment " + (++a) + ":";
});
}
.form-group {
display: inline
}
#wrapper > label {
margin: 0 0 10px 210px;
}
.segment {
display: inline-block;
margin: 0 0 1em
}
.form-group > label {
margin: 0 0 10px 20px;
}
.form-group > input {
width: 15%
}
<div class="container">
<h2>Form</h2>
<form>
<div id="room_fields">
<div class="content" id="wrapper">
<label style:>Segment 1:</label>
<div class="segment">
<div class="form-group">
<label>IN:</label>
<input name="seg-in[]" type="text">
</div>
<div class="form-group">
<label>OUT:</label>
<input name="seg-out[]" type="text">
</div>
<div class="form-group">
<label>Duration:</label>
<input name="seg-dur[]" type="text">
</div>
</div>
</div>
</div>
<br><br>
<div style="text-align: right;">
<button id="more_fields">+</button>
</div>
<br>
<br>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
innerHTML will not include the current value entered IIRC but it's still strange that doing += operation will remove the existing value.
However, insertAdjacentHTML() should work as expected.
var template;
var a = 1;
window.onload = function() {
template = document.querySelector("#wrapper").innerHTML;
document.querySelector("#more_fields").addEventListener("click", function(e) {
e.preventDefault(); // tell the browser to not send the form
document.getElementById('wrapper').insertAdjacentHTML('beforeend', template); // add next segment
document.querySelector("#wrapper > label:last-of-type").innerHTML = "Segment " + (++a) + ":";
});
}
.form-group {
display: inline
}
#wrapper > label {
margin: 0 0 10px 210px;
}
.segment {
display: inline-block;
margin: 0 0 1em
}
.form-group > label {
margin: 0 0 10px 20px;
}
.form-group > input {
width: 15%
}
<div class="container">
<h2>Form</h2>
<form>
<div id="room_fields">
<div class="content" id="wrapper">
<label style:>Segment 1:</label>
<div class="segment">
<div class="form-group">
<label>IN:</label>
<input name="seg-in[]" type="text">
</div>
<div class="form-group">
<label>OUT:</label>
<input name="seg-out[]" type="text">
</div>
<div class="form-group">
<label>Duration:</label>
<input name="seg-dur[]" type="text">
</div>
</div>
</div>
</div>
<br><br>
<div style="text-align: right;">
<button id="more_fields">+</button>
</div>
<br>
<br>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
Basically, my below code is not 100% correct, you should alter it by yourself following mine.
In the HTML, you can define a hidden div which is your wrapper. In its and nested element ids, you can set a pattern like '$$$'.
<div class="content" id="wrapper$$$" sytle="visibility: hidden;">
<label style:>Segment 1:</label>
<div class="segment">
<div class="form-group">
<label>IN:</label>
<input name="seg-in[]" type="text">
</div>
<div class="form-group">
<label>OUT:</label>
<input name="seg-out[]" type="text">
</div>
<div class="form-group">
<label>Duration:</label>
<input name="seg-dur[]" type="text">
</div>
</div>
</div>
In your javascript, declare a global variable named index and replace index value with '$$$'. It will be increased 1 when you add your template dynamically.
template = document.querySelector("#wrapper").innerHTML;
template = template.replace('$$$', index);
index ++;
...
Problem:
The problem here is with using innerHTML, because innerHTML will always override the HTML of your elements so previously typed values will be cleared, that's why you should use .appendChild().
And your logic for dynamic is correct, you just need to chnage the way you add new fields.
Solution:
I tried to rewrite your code so it uses appendChild() in a smart way using the #wrapper innerHTML as template and updating the id dynamically in the new appended fields.
var template = document.querySelector("#wrapper").innerHTML;
function addFields() {
var wrapper = document.createElement("div");
wrapper.innerHTML = template;
wrapper.querySelector("label:last-of-type").innerHTML = "Segment " + (++a) + ":";
document.getElementById('wrapper').appendChild(wrapper);
}
This code will create a new div everytime, wher we put the template HTML inside it, update the label dynamically referring the label inside our current wrapper div using wrapper.querySelector("label:last-of-type"), then finally append this new div to our element.
Demo:
Here's a working Demo snippet:
var template = document.querySelector("#wrapper").innerHTML;
var a = 1;
function addFields() {
var wrapper = document.createElement("div");
wrapper.innerHTML = template;
wrapper.querySelector("label:last-of-type").innerHTML = "Segment " + (++a) + ":";
document.getElementById('wrapper').appendChild(wrapper);
}
window.onload = function() {
document.querySelector("#more_fields").addEventListener("click", function(e) {
e.preventDefault();
addFields();
});
}
<div class="container">
<h2>Form</h2>
<form>
<div id="room_fields">
<div class="content" id="wrapper">
<label style:>Segment 1:</label>
<div class="segment">
<div class="form-group">
<label>IN:</label>
<input name="seg-in[]" type="text">
</div>
<div class="form-group">
<label>OUT:</label>
<input name="seg-out[]" type="text">
</div>
<div class="form-group">
<label>Duration:</label>
<input name="seg-dur[]" type="text">
</div>
</div>
</div>
</div>
<br><br>
<div style="text-align: right;">
<button id="more_fields">+</button>
</div>
<br>
<br>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>