I have a set of fields which can be duplicated. Now within those fields there are also particular fields which are duplicated.
This is an example of my markup
<div class="item_1">
<button type="button" class="fluid-inline btn btn-primary add_items">Add fields</button>
<hr />
<div class="col-sm-4 m-b-10px">
<label><h5>OCCUPATION: </h5></label>
<input name=b_occ[] type="text" class="form-control" required=required>
</div>
<div class="col-sm-4 m-b-10px">
<label><h5>WORK ADDRESS: </h5></label>
<input name=b_work_add[] type="text" class="form-control" required=required>
</div>
<div class="item_victim_relation">
<div class="col-sm-4 m-b-10px">
<label><h5>VICTIM: </h5></label>
<select class="form-control" name="victim_name[]" required=required>
<option value="victim_id"><h5>Victim</h5></option>
</select>
</div>
<div class="col-sm-4 m-b-10px">
<label><h5>RELATION TO VICTIM: </h5></label>
<input name=b_relation[] type="text" class="form-control" required=required>
</div>
<div class="col-sm-4 m-b-10px">
<h5 class="text-info">(IF SUSPECT HAS RELATION TO MULTIPLE VICTIMS) </h5>
<button type="button" class="dashed-button add_victim_b">Add</button>
</div>
</div>
</div>
JS
var removeButtonV = "<button type=button class='dashed-button remove_item_v'>Remove</button>";
var removeButton = "<button type=button class='btn btn-primary m-b-10px remove_items'>Remove</button>";
$('.add_victim_b').click(function() {
$('div.item_victim_relation:last').
after($('div.item_victim_relation:first').clone());
$('.add_victim_b:last').remove();
$('.text-info:last').text("(REMOVE THESE FIELDS)");
$(removeButtonV).insertAfter(('.text-info:last'));
});
$(document).on('click', '.remove_item_v', function(){
$(this).closest('div.item_victim_relation').remove();
});
$('.add_items').click(function() {
$('div.item_1:last').after($('div.item_1:first').clone());
$('div.item_1:last').append(removeButton);
$('hr.item_b_separator:last').removeClass('hidden');
});
$(document).on('click', '.remove_items', function(){
$(this).closest('div.item_1').remove();
});
This piece of code duplicates victim and relation to victim fields. It works fine when item_1 is not yet duplicated, but when it is, those two fields are appended on the last instance of the fields inside the last instance of item_1 too. What I want to happen is for these two fields to be appended after their last instances on the item_1 where they belong.
I think my way of traversal is wrong. I've tried several methods but I can't seem to get it work.
Here is a demonstration of the problem:
DEMO
Here is a sample of what I want (each has their own class names and item_1 cannot be duplicated but it would not work if item_1 would be cloned)
jQuery is only aware of the elements in the page at the time it runs, so new elements added to the DOM dynamically are unrecognized by jQuery. To combat the problem use event delegation, bubbling events from newly added items up to a point in the DOM which was there when jQuery ran on page load. Many people use document as the place to catch the bubbled event, but it isn't necessary to go all the way up the DOM tree. Ideally you should delegate to the nearest parent existing at the time of page load.
$(document).on('click', 'button.ok', function(e){...
Using the .on() function should solve your issue.
I managed to solve it by changing my traversal methods and just assigning the fields to be added in a variable.
I have changed the handler of add_victim_b to this
var victimFields = '<div class="item_victim_relation"><div class="col-sm-4 m-b-10px"><label><h5>VICTIM: </h5></label><select class="form-control" name="victim_name[]" required=required><option value="victim_id"><h5>Victim</h5></option></select></div><div class="col-sm-4 m-b-10px"><label><h5>RELATION TO VICTIM: </h5></label><input name=b_relation[] type="text" class="form-control" required=required></div><div class="col-sm-4 m-b-10px"><h5 class="text-info">(REMOVE FIELDS) </h5><button type="button" class="dashed-button remove_victim_b"><i class="fa fa-minus" aria-hidden=true></i></button></div><div class="clearfix"></div></div>';
$(document).on('click', '.add_victim_b', function(){
var item_b_index = $(this).closest('.item_b').index('.item_b');
$(victimFields).insertAfter($('div.item_b').eq(item_b_index).find('.item_victim_relation:last'));
});
It now works perfectly as I wanted it. You may view it here:
DEMO
Related
I have a form where the user can input multiple addresses, city, street+nbr and country.
For this field to be repeated I use the jquery repeater library. For the city field I want to use a selectize input field.
I am trying to repeat those 4 fields when clicking on the button, it copies everything correctly but the selectize field does not contain inputs (i guess this is because they have the same id?) but I don't know how to instantiate another selectize instance on that object.
This is my code:
HTML:
<div class="row">
<div class="form-group col-12 mb-2 address-repeater">
<div data-repeater-list="stcity">
<div class="input-group mb-1" data-repeater-item>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="companystreet"><?=lang("flow_company_street")?></label>
<input type="text" id="companystreet" class="form-control" placeholder="<?=lang("flow_company_streetname")?>" name="companystreet" required data-validation-required-message="<?=lang("flow_company_street_validation")?>">
<div class="help-block font-small-3"></div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="companystreetnumber"><?=lang("flow_company_nbr")?></label>
<input type="text" id="companystreetnumber" class="form-control" placeholder="<?=lang("flow_company_streetnbr")?>" name="companystreetnumber" required data-validation-required-message="<?=lang("flow_company_nbr_validation")?>">
<div class="help-block font-small-3"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="companycity"><?=lang("flow_company_city_or_commune")?></label>
<select id="companycity" class="companycity-select" name="companycity" autocomplete="new-password" required data-validation-required-message="<?=lang("flow_company_city_or_commune_validation")?>">
<option value="" selected><?=lang("flow_company_select_city_or_commune")?></option>
<?php
foreach ($citiesbe as $city) {
//Values are prefilled from javascript
$key = strtolower($city->name_nl) . "," . $city->zip_code;
echo "<option value=\"$key\"> $city->name_nl ($city->zip_code)</option>";
}
?>
</select>
<div class="help-block font-small-3"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="companycountry"><?=lang("flow_company_country")?></label>
<select id="companycountry" class="companycountry-select" name="companycountry" autocomplete="new-password" disabled>
<option value="BE" selected><?=lang("flow_company_country_belgium")?></option>
</select>
</div>
</div>
</div>
</div>
</div>
<button type="button" data-repeater-create class="btn btn-primary">
<i class="ft-plus"></i> Add new address
</button>
</div>
Javascript:
// Custom Show / Hide Configurations
$('.address-repeater').repeater({
show: function () {
$(this).slideDown();
},
hide: function(remove) {
$(this).slideUp(remove);
}
});
Since the button is not the selectize element, I don't know how to assign it to the newly created element.
Funny, I ran into a similar problem, but couldn't find a solution, so I had to work it out myself. Here's an explanation to the solution I applied to get selectize and jquery.repeater to work nicely.
First, checking through the browser console, you'll find out that selectize removes all the select options except the empty option, and uses it to populate it's own dropdown which is generated via javascript. This becomes a problem for jquery.repeater because it only repeats or creates a duplicate based on the initial page load, and not after. So, what gets repeated is the only select option left in the select element, which in this case (unfortunately) is the empty select option. Here's a pen explaining this, feel free to toggle the category targeting selectize in the select element to see for yourself.
So, here are the steps I took to get it to work nicely:
On repeating of the form (show() of the repeater instance), you'll need to delete the duplicated element completely from the DOM.
Create another select element(s) in the DOM with the preferred (or same) attributes/options.
Instantiate selectize on the newly created select element(s).
I'll suggest you add a class to the .form-group wrapper housing the .companycity-select select element. This will help to append a new select element at the exact place only, since there are other .form-group in the code. Check my solution below:
// Assuming your have the select element wrapper as <div class="form-group select-wrapper">
$('.address-repeater').reapeter({
show: function() {
$(this).slideDown();
// Remove the created element
$(this).find('.companycity-select').remove();
// Repeater rewrites your name attributes to avoid collisions within the same form,
// so we need to follow this same rule
// Get the length of the currently repeated items, to be used as the index for new elements
var count = $('div[data-repeater-item]').length;
// Create the new select element. The select name is based on the new name by repeater
var new_select_option = '<option value="" selected>Select city or community</option>';
var new_select_element = '<select id="companycity" class="companycity-select" name="stcity['+count+'][companycity]" autocomplete="new-password" required>'+new_select_option+'</select>';
// Append newly created element to DOM. Remember the new class added to the form-group?
$(this).find('.form-group.select-wrapper').append(new_select_element);
// Create a new instance of selectize on the new select element
$(this).find('.companycity-select').selectize({
// Populate your select options data via ajax,
// see docs: https://selectize.dev/docs.html
valueField: '',
labelField: '',
searchField: [],
options: []
});
}
});
I've found lots of examples of how to use jQuery filter on tables, but I can't find out how to do so on items not inside a table. For example, I have cards which I have to search through.
I have tried to adapt code from this w3schools example and it seems to work when I type the word 'second' but not the word 'first'.
$(document).ready(function() {
$("#txtSearch").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#milestoneCard").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
});
I was wondering if this was possible and if so what I'd have to change.
I've searched around quite a bit and while I'm sure it's possible, I can't seem to find out how in my case.
I have reproduced it in the following jsfiddle
You use the same id for two components.
Change it to class="col-md-4 milestoneCard" and your selector to $(".milestoneCard") and it will work.
The id property should be unique across the whole page and the selector will only grab the first one it finds.
You have multiple of the same id's on a page. Therefore the JS will not work as expected. ID's are unique, classes are able to be repeated.
to fix this, try making milestoneCard a class instead of an id in your html, as shown here
HTML
<input type="search" id="txtSearch">
<div class="col-md-4 milestoneCard">
<div class="card card-body bg-light" style="margin-bottom: 1em;">
<h3 id="searchTitle">First</h3>
<p>{{description}}</p>
<p>Due date:{{dueDate}}</p>
<p>Completed date:{{compDate}}</p>
<form action="/plannerHomepage" method="POST">
<input type="hidden" name="compId" value="{{id}}">
<input type="submit" role="button" class="btn btn-primary btn-lg" value="completed" style="margin-top:1em;" />
</form>
</div>
</div>
<div class="col-md-4 milestoneCard">
<div class="card card-body bg-light" style="margin-bottom: 1em;">
<h3 id="searchTitle">Second</h3>
<p>{{description}}</p>
<p>Due date:{{dueDate}}</p>
<p>Completed date:{{compDate}}</p>
<form action="/plannerHomepage" method="POST">
<input type="hidden" name="compId" value="{{id}}">
<input type="submit" role="button" class="btn btn-primary btn-lg" value="completed" style="margin-top:1em;" />
</form>
</div>
</div>
And your JavaScript
$(document).ready(function() {
$("#txtSearch").on("keyup", function() {
var value = $(this).val().toLowerCase();
$(".milestoneCard").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
});
Or see the jsfiddle here
In the markup, i have several divs with same id and inside those divs there are paragraphs and buttons. Now when a button is clicked, i want to get the value of a corresponding paragraph tag under the same div as that particular button. How can i do this with jQuery? The markup is as followed:
<div class="col-sm-5 narrow">
<p id="title">Jhon123</p>
<p id="text">This is the status of jhon</p>
<p>posted at 12:30pm GMT6+</p>
<form class="form-inline">
<input type="text" class="form-control" id="reply" placeholder="Type and enter to reply">
<button type="button" class="btn btn-default" id="repost">Re-Tweet</button>
</form>
</div>
When the button with the id #repost is clicked, i want to access the html inside the p tag with the id #text. I tried something like this:
$('#retweet').click(function(e){
e.stopPropagation();
var text = $(this).parent("div").closest('#text');
alert("some retweet button has been pressed which has the text:"+text);
});
You can use the jQuery .closest() function to get the containing <div> and then find the <p> tag you want inside it:
$('#repost').on('click', function () {
var text = $(this).closest('div[class^=col]').find('#text').html();
console.log(text);
});
The div[class^=col] selector means "find the closest div tag with a class starting with col". This allows you to use the other bootstrap column classes as well and have it still work.
$('#repost').click(function(){
console.log($(this).closest('div').find('#text').html());
});
See demo http://jsbin.com/wojupoyosa/1/edit?html,js,console,output
and as comments suggest you IDs should be unique per page so you should use a class or something else instead.
$( "#text" ).text() will give you the value inside P tag. So your code will look something like:
$('#repost').click(function(){
$( "#text" ).text() // save it to wherever you want
});
As a side note it is generally frowned upon to have css id's that are not unique - shared identifiers should use a class.
If you change all your ids into classes as shown in the demo below, then the following code should work fine. Also, you do not need the form element.
$('.repost').click(function(){
var text = $(this).closest('div').find('.text').text();
alert("some retweet button has been pressed which has the text: " + text);
});
$(function() {
$('.repost').click(function(){
var text = $(this).closest('div').find('.text').text();
alert("some retweet button has been pressed which has the text: " + text);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="col-sm-5 narrow">
<p class="title">Jhon123</p>
<p class="text">This is the status of jhon</p>
<p>posted at 12:30pm GMT6+</p>
<form class="form-inline">
<input type="text" class="form-control reply" placeholder="Type and enter to reply">
<button type="button" class="btn btn-default repost">Re-Tweet</button>
</form>
</div>
<div class="col-sm-5 narrow">
<p class="title">Mary123</p>
<p class="text">This is the status of mary</p>
<p>posted at 12:35pm GMT6+</p>
<form class="form-inline">
<input type="text" class="form-control reply" placeholder="Type and enter to reply">
<button type="button" class="btn btn-default repost">Re-Tweet</button>
</form>
</div>
I have multiple divs with the same class-'.new-contact.clearfix.invite'. The divs contain some inputs.
I want to perform validation on the form (which its class is 'invitePartners) only when I click/enter some input in different line (each div is a different line).
I tried the following, but the event is not triggered:
$('.invite').on('blur','.new-contact.clearfix.invite', function () {
$('.invitePartners').valid()
})
Example of the div's HTML:
<div class="row">
<div class="new-contact clearfix invite">
<div class="first-name invite">
<input type="text" id="firstName1" class="signup-input firstName required" name="first[1]" placeholder="">
</div>
<div class="last-name">
<input type="text" id="lastName1" class="signup-input lastName" name="last[1]" placeholder="optional">
</div>
<input type="text" data-index="1" id="inputMail1" class="signup-input mail" name="email[1]" placeholder="e.g. example#url.com" aria-invalid="true">
<span class="common-sprite sign-up-cross first clone"></span>
</div>
</div>
</div>
Quote from JQuery on help page :
The focus and blur events are specified by the W3C to not bubble, but jQuery defines cross-browser focusin and focusout events that do bubble.
This mean that your blur event don't go up the DOM when fired from .new-contact.clearfix.invite and so cannot be catch up by .invite
$('body').on('blur','.new-contact.clearfix.invite input', function () {
$('.invitePartners').valid()
});
Try .focusout() ...
$('.new-contact.clearfix.invite').focusout(function () {
$('.invitePartners').valid();
});
It's not really clear what your elements are, you should post your HTML in your question. If you are just trying to use elements that are related to the element blurred, you may need to use something like ...
$('.invitePartners', this).valid();
This is my first post on this site so hopefully you will go easy on me. I'm trying to create an HTML / PHP form and use a small piece of Javascript to add additional rows to a table when a button is clicked and increment the ID for the two fields.
The button works in adding the rows however it doesn't seem to increment the ID, just use the same ID as the previous row. Hopefully someone could help?
$(window).load(function(){
var table = $('#productanddates')[0];
var newIDSuffix = 2;
$(table).delegate('#button2', 'click', function () {
var thisRow = $(this).closest('tr')[0];
var cloned = $(thisRow).clone();
cloned.find('input, select').each(function () {
var id = $(this).attr('id');
id = id.substring(0, id.length - 1) + newIDSuffix;
$(this).attr('id', id);
});
cloned.insertAfter(thisRow).find('input:date').val('');
newIDSuffix++;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="blue-bar ta-l">
<div class="container">
<h1>Submit Your Insurance Renewal Date(s)</h1>
</div>
</div>
<div class="grey-bar">
<div class="container">
<div class="rounded-box">
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name" autocomplete="off" required />
</div>
<div>
<label for="name">Renewal Dates</label>
</div>
<table width="100%" border="0" cellspacing="0" cellpadding="5" id="productanddates" class="border">
<tr>
<td>
<select name="insurance_type1" id="insurance_type1">
<option></option>
<option>Car</option>
<option>Home</option>
<option>Van</option>
<option>Business</option>
<option>GAP</option>
<option>Travel</option>
</select>
</td>
<td>
<input type="date" name="renewal_date1" id="renewal_date1" />
</td>
<td>
<input type="button" name="button2" id="button2" value="+" />
</td>
</tr>
</table>
<div>
<label for="telephone_number">Contact Number</label>
<input type="tel" id="telephone_number" name="telephone_number" pattern="\d{11}" autocomplete="off" required />
</div>
<div>
<label for="email">Email Address</label>
<input type="email" id="email" name="email" autocomplete="off" required />
</div>
<div>
<input name="submit" type="submit" value="Submit" class="btn">
</div>
</div>
cloned.insertAfter(thisRow).find('input:date').val('');
This line isn't correct. It will throw an invalid selector error.
You need to change it to:
cloned.insertAfter(thisRow).find('input[type="date"]').val('');
jQuery actually does support the :INPUT-TYPE format in selectors, but not the new HTML5 input types (yet): so using input[type="date"] here is the correct way for now to select an element with an HTML5 type. Please notice the quotes around the value. If you want to select an attribute with a certain value.
A selector overview of css selectors here: W3schools.
Because this line is throwing an error your newIDSuffix never gets updated, because the script halts at the line before that because of the script error.
#Charlietfl raises a valid point about learning more about classes and DOM traversal. However that will not fix this code. Or explain why your code isn't working. Nevertheless it's a good tip.
I've gone ahead an taken a stab at a cleaner version of what I think that you are trying to accomplish. I'll walk through the major updates:
Updated the button id and name from "button2" to "button1" - I assumed that you would want to keep the indices in sync across the inputs in each row.
Changing $(window).load(function() { to $("document").ready(function() { - While either will work, the former will wait until all images have finished loading, while the latter while fire once the DOM has completed building. Unless you REALLY want the images to load first, I'd recommend $("document").ready(), for faster triggering of the code.
Removing the [0] references - the primary reason to use [0] after a jQuery selector collection is to reference the DOM version of the selected jQuery element, in order to us a "vanilla" JavaScript method on it. In all cases, you were re-rwapping the variables in $(...), which just converted the DOM element back into a jQuery object, so that extra step was not needed.
Changed the .delegate() method to .on() - as Howard Renollet noted, that is the correct method to use for modern versions of jQuery. Note that the "event" and "target" parameters have swapped places in on, from where they were in delegate.
Changed the event target from #button2 to :button - this will make sure that all of the buttons in the new rows will also allow you to add additional rows, not just the first one.
Switched the clone target from the clicked row to the last row in the table - this will help keep your row numbering consistant and in ascending order. The cloned row will always be the last one, regardless of which one was clicked, and the new row will always be placed at the end, after it.
Changed the indexing to use the last row's index as the base for the new row and use a regular expression to determine it - with the table being ordered now, you can always count on the last row to have the highest index. By using the regular expression /^(.+)(\d+)$/i, you can split up the index value into "everything before the index" and "the index (i.e., on or more numbers, at the end of the value)". Then, you simply increment the index by 1 and reattach it, for the new value. Using the regex approach also allows you to easily adapt, it there ever get to be more than 9 rows (i.e., double-digit indices).
Updated both the id and name attributes for each input - I assumed that you would want the id and name attributes to be the same for each individual element, based on the initial row, and, you were only updating the id in your code, which would have caused problems when sending the data.
Changed $("input:date") to $("input[type='date']) - as Mouser pointed out, this was really the core reason why your code was failing, initially. All of the other changes will help you avoid additional issues in the future or were simply "code quality"-related changes.
So . . . those were the major updates. :) Let me know if I misunderstood what you were trying to do or if you have any questions.
$("document").ready(function() {
$('#productanddates').on('click', ':button', function () {
var lastRow = $(this).closest('table').find("tr:last-child");
var cloned = lastRow.clone();
cloned.find('input, select').each(function () {
var id = $(this).attr('id');
var regIdMatch = /^(.+)(\d+)$/;
var aIdParts = id.match(regIdMatch);
var newId = aIdParts[1] + (parseInt(aIdParts[2], 10) + 1);
$(this).attr('id', newId);
$(this).attr('name', newId);
});
cloned.find("input[type='date']").val('');
cloned.insertAfter(lastRow);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="blue-bar ta-l">
<div class="container">
<h1>Submit Your Insurance Renewal Date(s)</h1>
</div>
</div>
<div class="grey-bar">
<div class="container">
<div class="rounded-box">
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name" autocomplete="off" required />
</div>
<div>
<label for="name">Renewal Dates</label>
</div>
<table width="100%" border="0" cellspacing="0" cellpadding="5" id="productanddates" class="border">
<tr>
<td>
<select name="insurance_type1" id="insurance_type1">
<option></option>
<option>Car</option>
<option>Home</option>
<option>Van</option>
<option>Business</option>
<option>GAP</option>
<option>Travel</option>
</select>
</td>
<td>
<input type="date" name="renewal_date1" id="renewal_date1" />
</td>
<td>
<input type="button" name="button1" id="button1" value="+" />
</td>
</tr>
</table>
<div>
<label for="telephone_number">Contact Number</label>
<input type="tel" id="telephone_number" name="telephone_number" pattern="\d{11}" autocomplete="off" required />
</div>
<div>
<label for="email">Email Address</label>
<input type="email" id="email" name="email" autocomplete="off" required />
</div>
<div>
<input name="submit" type="submit" value="Submit" class="btn">
</div>
</div>
cloned.insertAfter(thisRow).find('input[type="date"]').val('');