Detect input click with dynamic id using JQuery - javascript

In the website is the ability to ask a question and provide answers with images. We have used some website template and now trying to change functionallity. So I'm trying to achieve if image clicked, pop ups upload window (to select picture) and after clicking ok button, it should be shown near the answer (look image below, noted in red).
There is the ability to add more input fields by clicking the button. It generating HTML like this:
<div class="row">
<div class="another-div">
<div class="image-answer">
<input type="image" name="answers_images[]" id="test-1" src="image.png">
<input type="file" name="answers_images[]" id="file-1" style="display: none;">
</div>
<label class="text-answer">
<input type="text" name="answers[]" placeholder="Type here your answer">
</label>
</div>
</div>
If user clicks the button it will add another row with incremented ids, so it will be test-2, file-2
after clicking button once again it will add row with incremented ids once again test-3, file-3 and so on.
How can I detect input clicks (via JQuery) if ids are auto incremental? And after it do some changes with JQuery only with clicked ($this) image.
Something like this:
$("#file-1...").click(function(e) {
e.preventDefault();
// Some code here, I have working code to achieve what I want
// But problem, that I have no idea how to detect those clicks with dynamic ids
});
$("#test-1").change(function(e) {
e.preventDefault();
});

If you want to do event handling on dynamically added content, the trick is using the .on() method with a non-dynamically added parent element, and providing a selector argument. This approach use classes instead of ids. You cans still have ids, but the this variable becomes the element selected, so you don't really need them.
<div class="parent">
<div class="image-answer dynamically-added" id="answer1"></div>
<div class="image-answer dynamically-added" id="answer2"></div>
<div class="image-answer dynamically-added" id="answer3"></div>
</div>
$('.parent').on('click', '.image-answer', function() {});
Say if you clicked on the second .image-answer, in the event handler function this would be the same as $('#answer2')[0]. So to make it a jquery object you just have to $(this). And if you want to access a child elemet, just use the .find() method.
$(this).find('input[type="file"]').on('change', function previewFile() { ... })
Also, inputs dont' have a default action for click, so e.preventDefault() isn't necessary.

You can use class selector instead of id's to have a more dynamic approach of what you are trying to achieve. Also with class you will be writing less code and getting the same results.
You can still use the id's increment system to make sure the id's are unique in the DOM.
Add class to your input image and input file and and watch a change on those classes. You can use prev() method you select the previous input of which the change was made on using $(this)
//Load preview image on each answer
$(document).on('click', '.add_image', function(e) {
e.preventDefault()
//add file to input / trigger click
$(this).next('input').click();
})
//Watch the change on select file
$(document).on('change', '.select_file', function(e) {
e.preventDefault()
//get the sibling img src of the change file
var prevInput = $(this).prev('input')
//Call read file preview
previewFile(this, prevInput);
});
Pass your input and prev input image where the change happened to your previewImage function so that the file can be previewed in the image location using file Reader API.
//Preview img
function previewFile(input , previewSrc) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
//load file into actual preview img
$(previewSrc).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]); // convert to base64 string
}
}
I have added the functionality of adding more answers as well and you can upload image and preview images on those new answers as well using event Delegation as the elements are being append dynamically.
Complete Working Demo:
//row with incremented ids
var counter = 2
//Add more
$('#add_more').click(function() {
//add new answer
var newRow = `<div class="another-div">
<div class="image-answer">
<input type="image" class='add_image' name="answers_images[]" id="test-` + counter + `" src="https://img.icons8.com/dotty/80/000000/upload.png">
<input type="file" class='select_file' name="answers_images[]" id="file-` + counter + `" style="display: none;">
</div>
<label class="text-answer">
<input type="text" name="answers[]" id="answer-` + counter + `" placeholder="Type here your answer">
</label>
<button class="remove_answer"><i class="fas fa-window-close"></i></button>
</div>`
//append new answers
$('.row').append(newRow)
counter++ //increare counter
})
//remove answer
$(document).on('click', '.remove_answer', function() {
//remove the answer div
$(this).parent().remove()
counter-- //decrease counter
})
//Load preview image on each answer
$(document).on('click', '.add_image', function(e) {
e.preventDefault()
//add file to input / trigger click
$(this).next('input').click();
})
//Watch the change on select file
$(document).on('change', '.select_file', function(e) {
e.preventDefault()
//get the sibling img src of the change file
var prevInput = $(this).prev('input')
//Call read file preview
previewFile(this, prevInput);
});
//Preview img
function previewFile(input, previewSrc) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
//load file into actual preview img
$(previewSrc).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]); // convert to base64 string
}
}
.add_image {
width: 80px;
height: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<div class="row">
<div class="another-div">
<div class="image-answer">
<input type="image" class='add_image' name="answers_images[]" id="test-1" src="https://img.icons8.com/dotty/80/000000/upload.png">
<input type="file" class="select_file" name="answers_images[]" id="file-1" style="display: none;">
</div>
<div id="preview_img"></div>
<label class="text-answer">
<input type="text" name="answers[]" id="answer-1" placeholder="Type here your answer">
</label>
</div>
</div>
<br>
<button id="add_more">
Add New Answer
</button>

Related

How to remove input group in bootstrap?

This is what I have right now. I'm trying to add fields to the form dynamically using jQuery add() and append() method. But I want to remove the particular added field when the remove button is clicked.
<div class="col-md-12">
<h3>Added Description Fields</h3>
<div class="col-md-12" id="descFields">
</div>
</div>
JS
$(document).ready(function() {
console.log(descFields);
$('#addDesc').click(function(e) {
var descFields = $('#descFields');
var descLabel = $('#descLabel').val();
var large = '<div class="form-group" id="descField"><div class="input-group"><input type="text" class="form-control" placeholder="Enter Value For ' + descLabel + '" /><span class="input-group-btn"><button class="btn btn-danger" id="removeDesc" type="button">Remove</button></span></div>';
descFields.add(large).appendTo(descFields);
e.preventDefault();
});
$('#removeDesc').click(function(e) {
$(this).remove();
});
});
When the user click on the #removeDesc button , the the field that is added should be removed. I cannot figure out how to achieve this.
There are many ways of doing this, but the simpler for your problem is this one:
$(document).ready(function() {
console.log(descFields);
$('#addDesc').click(function(e) {
var descFields = $('#descFields');
var descLabel = $('#descLabel').val();
var large = '<div class="form-group" id="descField"><div class="input-group"><input type="text" class="form-control" placeholder="Enter Value For ' + descLabel + '" /><span class="input-group-btn"><button class="btn btn-danger" id="removeDesc" type="button">Remove</button></span></div>';
descFields.add(large).appendTo(descFields);
e.preventDefault();
});
$('#descFields').on('click', '#removeDesc', function(e) {
$(this).parents('.form-group').remove();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="descLabel"/>
<button id="addDesc">Add Desc</button>
<div class="col-md-12">
<h3>Added Description Fields</h3>
<div class="col-md-12" id="descFields">
</div>
</div>
Your problem is in the callback to delete the rows. When the document has finished loading you are trying to attach a click event to an object #removeDesc that is still not present in the DOM because it's created on the fly when the user clicks the #addDesc.
That's why you should use:
$('#descFields').on('click', '#removeDesc', function(e) {
$(this).parents('.form-group').remove();
});
As #vijayP suggested before you can use the on() to attach an event handler to the container where you'll be adding the object that is still not present in the DOM. Then you pass in the query selector as the second parameter to filter in execution time which of its children will trigger the event and execute the callback.
My additional trick is that I'm using .parents('.form-group') to select the div containing the group and remove all of the fields that were added instead of removing only the button.
Happy coding!
Add click event for remove button like follows:
$(document).on("click","#removeDesc",function(e) {
$(this).remove();
});

Use label to select image file but have attribute displayed input

I am looking to have the label be used for the selection of the item (so Choose File) is gone.
Here is my HTMl:
<div class="form-group">
<label class="btn btn-primary trigger" for="broker_image">Upload Image</label>
<input style="display:none;" class="uploadBtn" type="file" name="broker[image]" id="broker_image">
</div>
It works, although I do not have the image to actually have the name shown (which is part of the input tag)
Here is the js to try to make it show (which is 100% editable):
document.getElementsByClassName("trigger").onClick = function () {
document.getElementsByClassName("uploadFile").style.display = 'block';
document.getElementsByClassName("uploadFile").value = this.value;
};
Tried:
document.getElementsByClassName("trigger").onClick = function () {
document.getElementsByClassName("uploadFile")[0].style.display = 'block';
document.getElementsByClassName("uploadFile")[0].value = this.value;
};
With no luck.
JSFiddle:
http://jsfiddle.net/07zw6h3q/
I am lost on to make just the file name show, without some crazy javascript add in. Something simple. Using Rails 4.2 and Bootstrap.
You need to use the change event.
$('.uploadBtn').change(function(){
var a = $(this).val().split('\\');
$('.trigger').html(a[a.length - 1]);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="form-group">
<label class="btn btn-primary trigger" for="broker_image">Upload Image</label>
<input style="display:none;" class="uploadBtn" type="file" name="broker[image]" id="broker_image">
</div>
Also you can show a preview of the file (if it's image of course) Preview an image before it is uploaded
JSFiddle
HTML
<div class="form-group">
<label class="btn btn-primary trigger" for="broker_image" style="border : solid 1px lightgrey; padding : 5px">Upload Image</label>
<input style="display:none"; class="uploadBtn" type="file" name="broker[image]" id="broker_image" />
<label id="broker_image_name">no selected file</label>
</div>
JavaScript
var upload_button = document.getElementById("broker_image");
upload_button.addEventListener("change", function() {
var file_name = upload_button.value;
/*
the file come like this : C:\fakepath\file.htm
so we only need the file name
*/
file_name = file_name.split("\\"); // preventing \ to be escaped himself
var file_name_length = file_name.length;
file_name = file_name[file_name_length - 1];
var broker_image_name = document.getElementById("broker_image_name");
broker_image_name.innerHTML = file_name;
});
I used the event change to change the file name, which is an additional label I put right after your button (that I stylized a bit by the way) and that is updated with the file name.
You see a commented part for the file name value because when getting back the file name after the user select one, it comes with the pattern C:\fakepath\myFile.htm so I extracted only the file by splitting the string.

Issue finding specific div with same class - JQuery

I have a file selector that populates a div with a thumbnail based on the file. Now I have multiple buttons and divs that contain the same class names. What I am using for each time the primary button is selected is, var thisButton = $(event.target); which translates to:
var appendThis = thisButton.closest('.insurance').find(".attachmentCont");
appendThis.append('<div class="singleImg"><img src="'+attachment.url+'" width="100px"/><input type="hidden" class="fileURL" value="'+attachment.id+'"/><span class="removeImg remove"> X </span></div>');
$(".removeImg").bind('click', function(){
$(this).parent().remove();
});
The appenThis equals the button which is within a div containing another div attachmentCont Both the button and div are within a parent div which is div.insurance There are multiple div.insurance containers. My issue is when using thisButton.closest('.insurance').find(".attachmentCont"); it always focuses on the .attachmentCont div that was first selected. So if I selected section 1, the image populates. If I select section 2, section 1 still gets that image when the div.attachmentCont within section 2 should of gotten the append() too.
Suggestions or Thoughts?
HTML EXAMPLE:
<div class="notes-section insurance">
<span><b>General </b></span><textarea style="width:97%;" rows="5" cols="50" class="insurance-description fields contractor_insurance_description" placeholder="...."></textarea>
<input type="date" class="expiration-date" placeholder="Expiration Date">
<br>
<span>Your</span>
<div class="attachmentCont">
</div>
<br>
<input type="button" class="upload_image_button" value="Attach Documents" style="width:150px;">
</div>
Here was the issue:
My blindness did not see that the function that contains the selected button was not in any way associated with the function that appended the data. Because of this all I had to do was move my var thisButton outside into the parent class.
Thank you everyone for you're time.
Solutions:
var thisButton
$('.upload_image_button').on('click', function( event ){
thisButton = $(event.target);
event.preventDefault();
});
....
function...({
var appendThis = thisButton.closest('.insurance').find(".attachmentCont");
appendThis.append('<div class="singleImg"><img src="'+attachment.url+'" width="100px"/><input type="hidden" class="fileURL" value="'+attachment.id+'"/><span class="removeImg remove"> X </span></div>');
$(".removeImg").bind('click', function(){
$(this).parent().remove();
});
});

Delegated event handler for a set of dynamic elements

My JS is as follows:
$(document).ready(function(){
$('#data1').change(function(){
title = $('#title1').val();
url = $('#url1').val();
$.post('library/edit.php',{title:title, url:url},function(res){
alert ("updated !");
});
});
});
and my HMTL-markup:
<div id="data1">
<input name="title1" type="text" id="title1" />
<input name="url1" type="text" id="url1" />
</div>
I wrote that code to call to a PHP file on change of textbox.
that code works as expected.
But now I've added more textboxes as follows:
<div id="div1"><input name="title1" type="text" id="title1" />
<input name="url1" type="text" id="url1" /></div>
<div id="div2"><input name="title2" type="text" id="title2" />
<input name="url2" type="text" id="url2" /></div>
<div id="div3"><input name="title3" type="text" id="title3" />
<input name="url3" type="text" id="url3" /></div>
Now I want the same functionality so that any of these textboxes works like title1 in my earlier code. So if input#title-3 is changed I want the change to be uploaded via POST to my PHP-script.
Important: The number of boxes are dynamic.
$(document).ready(function(){
$('#data1').on('change','[id^=title],[id^=url]',function(){
var index = $(this).attr('id').replace('title',"").replace('url',"");
var title = $("#title" + index).val();
var url = $("#url" + index).val();
var hid = $("#hid" + index).val();
// you can put in here in sequence all the fields you have
$.post('library/edit.php',{title:title, url:url, hid : hid},function(res){
alert ("updated !");
});
});
});
so by this answer if any text box whoes id starts with title changes.
the function passed in will be invoked.
indezx variable will store the index of the group of the elements that are changing. and then is being callculated by removing title from title1 or title2
I think the answer you have it right here:
I wrote that code to call to php file on change of textbox.
That script (jQuery I guess) it must be associatte with the $('#xxx1').onchange() right? (or similar)
If you modify the function, add a class to the input field (also in the php) and each time you call the function, start listening again.. I think you can call any function you may want.
Example (guessing your code)
HTML
<div id="data1" class="dynamicDiv">
<input name="title1" type="text" id="title1" />
<input name="url1" type="text" id="url1" />
</div>
jQuery
// enable the function on document ready
$(document).ready(function(){
startListening('.dynamicDiv');
});
// each time an ajax call is completed,
// run the function again to map new objects
$(document).ajaxComplete(function(){
startListening('.dynamicDiv');
});
// and finally the function
function startListening(obj){
$(obj).onchange(function(){
// ajax call
// function call
// or whatever...
});
}
PHP
// some code....
// some code....
// remember here to add "dynamicDiv" as a class in the object
return object;
Since your elements are dynamically generated you need to use event delegation, then you can use [id^="value"] to select the appropriate elements based on the first part of their id attribute
$(document).ready(function(){
$(document).on('change','[id^="data"]',function(){
var title = $(this).find('[id^="title"]').val();
var url = $(this).find('[id^="url"]').val();
var hidden = $(this).find('[id^="hid"]').val();
$.post('library/edit.php',{title:title, url:url, hid:hidden},function(res){
alert ("updated !");
});
});
});
Note: I suggest you bind to the closest parent of your data divs that is present at page load instead of binding to the document

trouble with jquery and traversing the DOM to select the appropriate elements

Hello guys i have the below html for a number of products on my website,
it displays a line with product title, price, qty wanted and a checkbox called buy.
qty input is disabled at the moment.
So what i want to do is,
if the checkbox is clicked i want the input qty to set to 1 and i want it to become enabled.
I seem to be having some trouble doing this. Could any one help
Now i can have multiple product i.e there will be multiple table-products divs within my html page.
i have tried using jQuery to change the details but i dont seem to be able to get access to certain elements.
so basically for each table-product i would like to put a click listener on the check box that will set the value of the input-text i.e qty text field.
so of the below there could be 20 on a page.
<div class="table-products">
<div class="table-top-title">
My Spelling Workbook F
</div>
<div class="table-top-price">
<div class="price-box">
<span class="regular-price" id="product-price-1"><span class="price">€6.95</span></span>
</div>
</div>
<div class="table-top-qty">
<fieldset class="add-to-cart-box">
<input type="hidden" name="products[]" value="1"> <legend>Add Items to Cart</legend> <span class="qty-box"><label for="qty1">Qty:</label> <input name="qty1" disabled="disabled" value="0" type="text" class="input-text qty" id="qty1" maxlength="12"></span>
</fieldset>
</div>
<div class="table-top-details">
<input type="checkbox" name="buyMe" value="buy" class="add-checkbox">
</div>
<div style="clear:both;"></div>
</div>
here is the javascript i have tried
jQuery(document).ready(function() {
console.log('hello');
var thischeck;
jQuery(".table-products").ready(function(e) {
//var catTable = jQuery(this);
var qtyInput = jQuery(this).children('.input-text');
jQuery('.add-checkbox').click(function() {
console.log(jQuery(this).html());
thischeck = jQuery(this);
if (thischeck.is(':checked'))
{
jQuery(qtyInput).first().val('1');
jQuery(qtyInput).first().prop('disabled', false);
} else {
}
});
});
// Handler for .ready() called.
});
Not the most direct method, but this should work.
jQuery(document).ready(function() {
jQuery('.add-checkbox').on('click', function() {
jQuery(this)
.parents('.table-products')
.find('input.input-text')
.val('1')
.removeAttr('disabled');
});
});
use
jQuery('.add-checkbox').change(function() {
the problem is one the one hand that you observe click and not change, so use change rather as it really triggers after the state change
var qtyInput = jQuery(this).children('.input-text');
another thing is that the input is no direct child of .table-products
see this fiddle
jQuery('input:checkbox.add-checkbox').on('change', function() {
jQuery(this)
.parent()
.prev('div.table-top-qty')
.find('fieldset input:disabled.qty')
.val(this.checked | 0)
.attr('disabled', !this.checked);
});
This should get you started in the right direction. Based on jQuery 1.7.2 (I saw your prop call and am guessing that's what you're using).
$(document).ready(function() {
var thischeck;
$('.table-products').on('click', '.add-checkbox', function() {
var qtyInput = $(this).parents('.table-products').find('.input-text');
thischeck = $(this);
if (thischeck.prop('checked')) {
$(qtyInput).val('1').prop('disabled', false);
} else {
$(qtyInput).val('0').prop('disabled', true);
}
});
});
Removing the property for some reason tends to prevent it from being re-added. This works with multiple tables. For your conflict, just replace the $'s with jQuery.
Here's the fiddle: http://jsfiddle.net/KqtS7/5/

Categories