I have for example 3 fields that user can input a number a,b,c
So field C will check if number entered in field C is < a and > b.
In the form i have a button that create an additional line with
another a,b,c; so i don't know how to control same operation like before...
FIDDLE
$(".c").change(function() {
if ($('.c').val() > $('.a').val() || $('.c').val() < $('.b').val()) {
$('.c').css("backgroundColor", "#ff0000");
} else {
$('.c').css("backgroundColor", "#00FF00");
}
});
$('.add').click(function() {
var tr = '<tr>' + '<td>Cota1</td>' +
'<td><input type="text" name="tol_cota1[]" class="form-control a"></td>' +
'<td><input type="text" name="tolb_cota1[]" class="form-control b"></td>' +
'<td><input type="text" name="medido_cota1[]" class="form-control c"></td>' +
'<td><input type="button" class="btn btn-danger remove" value="Remove Line"></td>' + '</tr>';
$('.details').append(tr);
});
// delete row
$('.details').delegate('.remove', 'click', function() {
var con = confirm("Are you sure you want to remove the line?");
if (con) {
$(this).parent().parent().remove();
}
});
The change event doesn't bubble, which means you will need an event listener for every input in your form.
jQuery will take care of that automatically when using using the .on() method with a selector (its second parameter), which is equivalent to the old deprecated .delegate() method. From its description in the official docs, it will:
Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements.
So, if you do something like this:
$('.details').on('change', 'input', (event) => { ... });
This will listen for change events on any <input> element inside all elements matching the .details selector, regardless if they already existed when the method was called or if they were created afterwards as it is your case.
Now, once a change event occurs, you should use the .parent(), .eq and .find() methods to select the row in which the <input> that triggered the event is located, from there you get all 3 inputs based on their position or selector, their value, and do your logic to update that specific row.
Anyway, if instead of listening for change events you use input, which does bubble, you can benefit from event delegation. This means that a single event listener will be created for the whole <tbody> in this case, instead of one per <input>. Using event.target you will be able to distinguish which one triggered the event, which you need to use anyway to get the other inputs in the same row.
All together, it will look something like this:
// Better to keep the reference instead of getting it each time:
const details = $('#details');
details.on('input', 'input', (event) => {
const children = $(event.target).parents().eq(1).children();
const avgInput = children.eq(3).find('input');
const max = parseInt(children.eq(1).find('input').val());
const min = parseInt(children.eq(2).find('input').val());
const avg = parseInt(avgInput.val());
if (isNaN(max) ||isNaN(min)|| isNaN(avg)) {
// Don't do anything if any of them is blank.
return;
}
avgInput.css('backgroundColor', avg > max || avg < min ? '#ff0000' : '#00FF00');
});
details.on('click', '.remove', (event) => {
if (confirm('Are you sure you want to remove the line?')) {
$(event.target).parents().eq(1).remove();
}
});
$('#add').click(() => {
details.append(`
<tr>
<td>Cota1</td>
<td><input type="text"></td>
<td><input type="text"></td>
<td><input type="text"></td>
<td><button class="remove">DELETE</button></td>
</tr>
`);
});
body {
font-family: sans-serif;
font-size: .75rem;
}
table {
border-collapse: collapse;
table-layout: fixed;
position: relative;
}
table th,
table td {
width: 20%;
padding: 0;
border-bottom: 1px solid #EEE;
height: 1.75rem;
}
input {
width: 100%;
box-sizing: border-box;
padding: .5rem;
border: none;
outline: none;
text-align: center;
}
input:hover {
background: #FAFAFA;
}
input:focus {
background: #FFA;
}
.remove {
width: 100%;
box-sizing: border-box;
padding: .5rem;
border: none;
outline: none;
background: #FFF;
cursor: pointer;
}
.remove:hover {
background: #F44;
}
#add {
width: 100%;
border: none;
background: #FFF;
padding: .5rem;
border-radius: 2px;
color: #000;
text-transform: uppercase;
font-size: .75rem;
margin: 1rem 0 0;
box-shadow: 0 0 1rem rgba(0, 0, 0, .25);
transition: box-shadow ease-in .125s;
}
#add:hover {
box-shadow: 0 0 .5rem rgba(0, 0, 0, .25);
}
<table>
<thead>
<tr>
<th> </th>
<th>TOL +</th>
<th>TOL -</th>
<th>AVG</th>
<th></th>
</tr>
</thead>
<tbody id="details">
<tr>
<td>Cota1</td>
<td><input type="text"></td>
<td><input type="text"></td>
<td><input type="text"></td>
<td> </td>
</tr>
</tbody>
</table>
<button id="add">ADD</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
the best way would be using the closest function
$(".c").change(function(){
if($(this).val() > $(this).closest('.a').val() || $(this).closest('.c').val() < $('.b').val())
{
$(this).closest('.c').css( "backgroundColor", "#ff0000" );
}else{
$(this).closest('.c').css( "backgroundColor", "#00FF00" );
}
});
Related
I am trying to make a "My Favorite Movies" list page where users can add and rate movies. This program should include:
1) a form where you can add to the list and rate it
2) a table of all the things you've added
3) delete button for each row of the table that lets you remove elements from the list (what i'm having trouble on)
Instead of deleting only one row, it deletes every appended movie/rating in the table. Also if you click anywhere else, it deletes everything as well.
4) bonus: sort feature, so i can sort entries in the table by the their title or their rating.
example here: rithm school example
$(function() {
$('#addMovieButton').click(function() {
var addToTitle = $('#title').val();
$('#tableTitle').append('<tr><td>' + addToTitle + '</td></tr>');
var addToRating = $("#rating").val();
$('#tableRating').append('<tr><td>' + addToRating + '</td></tr>');
$('#tableDelete').append('<tr><td><input type="button" value="Delete All"</tr></td>');
$('#tableRow').on('click', function() {
$('#tableTitle').last().children().remove();
$('#tableRating').last().children().remove();
$('#tableDelete').last().children().remove();
});
});
});
h1 {
text-align: center;
}
table {
width: 100%;
border-radius: 10px;
}
table,
td {
border: 1px solid black;
padding: 15px;
}
th {
height: 50px;
text-align: center;
}
td {
text-align: center;
}
body {
font-family: helvetica;
}
form {
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<label><b>Title</b></label>
<input id="title" type="text" value="Movie Title">
<label><b>Rating</b></label>
<input id="rating" type="text" value="Rate The Movie from 0 to 10">
<button type='button' id="addMovieButton">Add Movie</button>
</form>
<table>
<tr id="tableRow">
<th id="tableTitle">Title</th>
<th id="tableRating">Rating</th>
<th id="tableDelete">Delete</th>
</tr>
</table>
<table> Structure
The structure of the appended "row" are not valid HTML. A <table> will have at least one <tbody>. If the user doesn't add it the browser will. Although most methods, function, and properties will treat the <table> as the direct parent of <tr>, there are some advantages to targeting <tbody> instead. If there's a <thead> then targeting the <tbody> can free you from extra steps trying to avoid the <th>.
Keep these rules in mind when structuring a <table>
<tbody> can only have <tr> as children (direct descendants)
<tr> can only have <td> and <th> as children
<td> and <th> can have anything as descendants.
Make sure rows are structured like so:
<tr><td</td>...<td></td></tr>
Add row to <table> or <tbody>
Demo
The following demo has detailed comments within the HTML, and CSS, as well as step by step details commented in the JavaScript
$(function() {
/*
Bind the <form> to the 'submit', 'click', and 'change' events.
Pass the Event Object thru
*/
$('form').on('submit click change', function(event) {
// Reference the type of event
let eType = event.type;
// if the 'submit' event was triggered...
if (eType === 'submit') {
// Stop the <form> from sending data to a server and resetting
event.preventDefault();
// Get the values of the <input>
let name = $('.title').val();
let rate = $('.rating').val();
// Declare a htmlString using a Template Literal
const row = `
<tr><td>${name}</td>
<td>${rate}</td>
<td><input class='sel' type='checkbox'>
</td></tr>`;
// Render the htmlString as the last child of the <tbody>
$('.data').append(row);
// Reset <form>
$(this).trigger('reset');
// ...otherwise if the 'click' event triggered...
} else if (eType === 'click') {
// ...and the clicked tag has class 'del'...
if ($(event.target).hasClass('del')) {
/*
Collect all checked <input class='sel'>
then on .each() one...
*/
$('.sel:checked').each(function(i) {
/*
Get the ancestor <tr> of the current .sel
and remove it
*/
$(this).closest('tr').remove();
});
// Reset the <form>
$('form').trigger('reset');
}
// ...otherwise if the 'change' event was triggered...
} else if (eType === 'change') {
// ...and the changed tag id is 'all'...
if (event.target.id === 'all') {
// Check if #all is checked or not
let allChk = $('#all').is(':checked');
// Loop thru each() <input class='sel'>...
$('.sel').each(function(i) {
/*
and check current .sel if #all is checked
or uncheck current .sel if #all is NOT checked
*/
$(this).prop('checked', allChk);
});
}
}
// Stop any events from bubbling any further up the event chain
event.stopPropagation();
});
});
:root {
font: 400 3vw/1.2 Arial;
}
form {
text-align: center;
}
table {
width: 100%;
border-radius: 10px;
table-layout: fixed;
margin: 12px auto
}
table,
td {
border: 1px solid black;
padding: 15px;
}
th {
height: 30px;
width: 20%;
}
th:first-of-type {
width: 60%;
}
td {
text-align: center;
}
button,
input,
label {
display: inline-block;
font-size: initial;
}
.all {
font-weight: 400;
padding: 3px 6px;
border: 1.5px inset rgba(0, 28, 255, 0.3);
margin-top: 3px;
}
.all::after {
content: 'Selected'
}
/*
When input#all is :checked the label.all that follows
#all will change
the content of its pseudo-element from 'Selected' to 'All'
*/
#all:checked+.all::after {
content: 'All'
}
button:hover {
cursor: pointer;
outline: 3px outset rgba(0, 28, 255, 0.4);
color: rgba(0, 28, 255, 0.6);
}
.all:hover {
cursor: pointer;
color: rgba(0, 28, 255, 0.8);
background: rgba(0, 28, 255, 0.2);
}
.rating {
text-align: right;
width: 4ch;
}
.title {
padding-left: 5px;
width: 27ch;
}
/*
The checkbox #all is not visible to user but is accessible through the label.all
which it is synced with (see comments in HTML
*/
#all {
display: none
}
<form>
<label>Title</label>
<!--
The [required] attribute enables built-in form validation
If the submit event is triggered
and either <input> is blank, the submit event is interrupted and
a tooltip will notify
user that the <input> cannot be empty
-->
<input class="title" type="text" placeholder="Pulp Fiction" required>
<label>Rating</label>
<!-- See previous comment -->
<input class="rating" type="number" min='0' max='10' placeholder="10" required>
<!--
<button type='submit'> or <input type='submit'>
or <button> within a <form> will trigger a submit event by default
-->
<button>Add Movie</button>
<table>
<thead>
<tr>
<th>Title</th>
<th>Rating</th>
<th>
<button class='del' type='button'>Remove</button>
<!--
A <label> and a form control (ie <input>, <textarea>, <select>, etc) can be synced by
matching the [for] attribute value to the form controls #id:
1. <label for='XXX'>
2. <input id='XXX'>
When synced, clicking one will remotely click the other
-->
<input id='all' type='checkbox'>
<label class='all' for='all'></label></th>
</tr>
</thead>
<!--
See post on <table> structure
-->
<tbody class='data'></tbody>
</table>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
you've put all titles in a parent, and all rating in another parent ,and all delete buttons in another one . you should place the information about each row in a parent and then you can delete by row easily.
(also you can add td,tbody it's just sample to showing the way)
$('#addMovieButton').click(function () {
var addToTitle = $('#title').val();
var addToRating = $("#rating").val();
$('#table').append('<tr><th>' + addToTitle + '</th><th>' + addToRating + '</th><th><input type="button" value="Delete All" class="tableDelete"></th></tr>');
$('.tableDelete').click(function () {
$(this).parents('tr').remove();
});
});
h1 {
text-align: center;
}
table {
width: 100%;
border-radius: 10px;
}
table,
td {
border: 1px solid black;
padding: 15px;
}
th {
height: 50px;
text-align: center;
}
td {
text-align: center;
}
body {
font-family: helvetica;
}
form {
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<label><b>Title</b></label>
<input id="title" type="text" value="Movie Title">
<label><b>Rating</b></label>
<input id="rating" type="text" value="Rate The Movie from 0 to 10">
<button type='button' id="addMovieButton">Add Movie</button>
</form>
<table id="table">
</table>
Question:
I have a HTML with type number i want to ge the value using jQuery create a range by adding 10% on either side then checking if each row falls in the range if not hide the row.
Code:
HTML
<input type="number" id="myPrice" onkeyup="myPriceFunction()" placeholder="Enter amount.." title="Type in a amount" min="0">
JavaScript/jQuery
$(document).ready(function(){
$("#myPrice").on("keyup", function() {
priceLow = $(this).val() * 0.9;
priceHigh = $(this).val() * 1.1;
});
});
JS fiddle
https://jsfiddle.net/nx30zqjd/7/
Other:
I am using:
.addClass('discarded').hide();
.removeClass('discarded').show();
to hide and show rows
Expected Results:
I want the range to be created with priceLow and priceHigh then get price column get rid of $ and check if it falls within range if not hide using code above.
Update:
I added
$(this).closest('tr').removeClass('discarded').show();
} else {
$(this).closest('tr').addClass('discarded').hide();
}
however then this shows nothing because i don't check discarded rows, if i remove the check for discarded rows it doesn't apply my search to the range not sure if there is a easy way to have both but it seems to be ok for the moment
If you need any more information please ask.
Thanks
You can use this, you just need to iterate and get the values and then check, based on that you can hide and show.
$("#myPrice").on("keyup", function() {
if ($(this).val() === '') {
$("#myTable tr").show();
return;
}
priceLow = $(this).val() * 0.9;
priceHigh = $(this).val() * 1.1;
$("#myTable tr td:nth-child(2)").each(function(e) {
var value = this.textContent.replace('$', '');
if (value >= priceLow && value <= priceHigh) {
$(this).closest('tr').show();
} else {
$(this).closest('tr').hide();
}
})
});
$(document).ready(function() {
});
$(document).ready(function() {
$("#myPrice").on("keyup", function() {
if ($(this).val() === '') {
$("#myTable tr").show();
return;
}
priceLow = $(this).val() * 0.9;
priceHigh = $(this).val() * 1.1;
$("#myTable tr td:nth-child(2)").each(function(e) {
var value = parseFloat(this.textContent.replace('$', ''));
if (value >= priceLow && value <= priceHigh) {
$(this).closest('tr').removeClass('discarded').show();
} else {
$(this).closest('tr').addClass('discarded').hide();
}
})
});
});
<style>* {
box-sizing: border-box;
}
#mySearch {
background-image: url('https://www.w3schools.com/css/searchicon.png');
background-position: 10px 10px;
background-repeat: no-repeat;
width: 100%;
font-size: 16px;
padding: 12px 20px 12px 40px;
border: 1px solid #ddd;
margin-bottom: 12px;
}
#myTable {
border-collapse: collapse;
width: 100%;
border: 1px solid #ddd;
font-size: 18px;
}
#myTable th,
#myTable td {
text-align: left;
padding: 12px;
}
#myTable tr {
border-bottom: 1px solid #ddd;
}
.show {
display: block;
}
a {
color: blue;
text-decoration: none;
/* no underline */
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h2>CPUs</h2>
CPU
Motherboards
<input type="number" id="myPrice" placeholder="Enter amount.." title="Type in a amount" min="0">
<input type="text" id="mySearch" placeholder="Search for cpus.." title="Type in a cpu name">
<table id='myTable'>
<thead>
<tr>
<th>CPU</th>
<th>Price</th>
<th>Mark</th>
<th>Value</th>
<th>Socket</th>
<th>Image</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href='mobo.php?cpu_name=AMD Ryzen 5 2600"' target='_blank'>AMD Ryzen 5 2600</a></td>
<td>$246.05</td>
<td>13537</td>
<td>55.02</td>
<td>AM4</td>
<td><img src=NA height='42' width='42'></td>
</tr>
</tbody>
<tbody>
<tr>
<td><a href='mobo.php?cpu_name=Intel Core i7-8700K"' target='_blank'>Intel Core i7-8700K</a></td>
<td>$585.90</td>
<td>15957</td>
<td>27.24</td>
<td>LGA1151</td>
<td><img src=". $row[" url "]." height='42' width='42'></td>
</tr>
</tbody>
</table>
I have a table with each row having a class ".clickablerow" and I defined onclick function when click on this row, a dialog will show up then I can insert above or below some text as new row. The question is, though I've added ".clickablerow" to these new added rows, they are actually not clickable and no such dialog showing up.
My code is like:
JS:
$(document).ready(function() {
$(".clickable-row").click(function(){
var i = $(this).rowIndex;
var html = "<tr class='clickable-row' > <td> a test </td></tr>";
$('#table_id > tbody > tr').eq(i).after(html);
});
HTML:
<head>
...
<link rel="stylesheet" href="../static/css/main.css">
<link rel="stylesheet" href="./static/css/bootstrap.min.css">
<script src="./static/js/jquery.min.js"></script>
<script src="./static/js/bootstrap.min.js"></script>
<script src="./static/js/jquery.expander.min.js" type="text/javascript"></script>
<script src="./static/js/main.js" type="text/javascript"></script>
....
<head>
<body>
....
<table>
<tbody>
<tr class='clickable-row' >
<td style="border: 0;"> Initial message ....</td>
</tr>
</tbody>
</table>
....
</body>
If I click on the row, a new row with class 'clickable-row' will be added, however, that new row is not clickable. Any idea are welcome!
Thanks
Just delegate the click event to the <table> or the <tbody>:
const tableBody = document.getElementById('tableBody');
tableBody.onclick = (e) => {
const target = e.target;
let row = target;
while (!row.classList.contains('clickable-row') && row !== tableBody) row = row.parentElement;
if (row === tableBody) {
return; // Ignore the click if we could not find a .clickable-row
}
const newRow = tableBody.insertRow(target.rowIndex);
newRow.className = Math.random() < 0.5 ? 'clickable-row' : 'disabled-row';
newRow.innerHTML = '<td>...</td>';
};
table {
font-family: monospace;
width: 100%;
border-collapse: collapse;
text-align: center;
user-select: none;
}
tr.disabled-row {
background: #EEE;;
}
tr.clickable-row {
cursor: pointer;
}
tr.clickable-row:hover {
background: yellow;
}
td {
border: 2px solid black;
padding: 8px 4px;
}
<table>
<tbody id="tableBody">
<tr class="clickable-row"><td>...</td></tr>
</tbody>
</table>
With jQuery, you can use .on():
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers. This element could be the container element of a view in a Model-View-Controller design, for example, or document if the event handler wants to monitor all bubbling events in the document. The document element is available in the head of the document before loading any other HTML, so it is safe to attach events there without waiting for the document to be ready.
In addition to their ability to handle events on descendant elements not yet created, another advantage of delegated events is their potential for much lower overhead when many elements must be monitored. On a data table with 1,000 rows in its tbody, this example attaches a handler to 1,000 elements:
$( "#dataTable tbody tr" ).on( "click", function() {
console.log( $( this ).text() );
});
An event-delegation approach attaches an event handler to only one element, the tbody, and the event only needs to bubble up one level (from the clicked tr to tbody):
$( "#dataTable tbody" ).on( "click", "tr", function() {
console.log( $( this ).text() );
});
The previous example would then look like this:
$('#tableBody').on('click', '.clickable-row', (e) => {
$(e.currentTarget).after(`<tr class=${ Math.random() < 0.5 ? 'clickable-row' : 'disabled-row' }><td>...</td></tr>`);
});
table {
font-family: monospace;
width: 100%;
border-collapse: collapse;
text-align: center;
user-select: none;
}
tr.disabled-row {
background: #EEE;;
}
tr.clickable-row {
cursor: pointer;
}
tr.clickable-row:hover {
background: yellow;
}
td {
border: 2px solid black;
padding: 8px 4px;
}
<table>
<tbody id="tableBody">
<tr class="clickable-row"><td>...</td></tr>
</tbody>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Create a "delegated" binding by using on().
Delegated events have the advantage that they can process events from
descendant elements that are added to the document at a later time.
$('#table_id').on('click', '.clickable-row', function() {
debugger;
var i = this.rowIndex;
var html = "<tr class='clickable-row' > <td> a test " + i + "</td></tr>";
$('#table_id > tbody > tr').eq(i).after(html);
});
table#table_id {
border-collapse: collapse;
}
#table_id tr {
background-color: #eee;
border-top: 1px solid #fff;
}
#table_id tr:hover {
background-color: #ccc;
}
#table_id th {
background-color: #fff;
}
#table_id th,
#example td {
padding: 3px 5px;
}
#table_id td:hover {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="table_id">
<tbody>
<tr class='clickable-row' data-href='url://'>
<td style="border: 0;"> Initial message ....</td>
</tr>
</tbody>
</table>
I'm wondering if it's possible to on each appendTo make the new div unique but still use the same jquery.
As you can see in the mark-up below, each new div shares the same jquery so doesn't work independently.
Within my Javascript i'm selecting the ID to fire each function.
I've tried just adding + 1 etc to the end of each ID, but with that it changes the name of the ID making the new created DIV not function.
I've thought of using DataAttribues, but i'd still have the same issue having to create multiple functions all doing the same job.
Any ideas?
Thanks
$(function() {
var test = $('#p_test');
var i = $('#p_test .upl_drop').length + 1;
$('#addtest').on('click', function() {
$('<div class="file-input"><div class="input-file-container upl_drop"><label for="p_test" class="input-file-trigger">Select a file...<input type="file" id="p_test" name="p_test_' + i + '" value=""class="input-file"></label></div><span class="remtest">Remove</span><p class="file-return"></p></div>').appendTo(test);
i++;
});
$('body').on('click', '.remtest', function(e) {
if (i > 2) {
$(this).closest('.file-input').remove();
i--;
}
});
});
var input = document.getElementById( 'file-upload' );
var infoArea = document.getElementById( 'file-upload-filename' );
input.addEventListener( 'change', showFileName );
function showFileName( event ) {
// the change event gives us the input it occurred in
var input = event.srcElement;
// the input has an array of files in the `files` property, each one has a name that you can use. We're just using the name here.
var fileName = input.files[0].name;
// use fileName however fits your app best, i.e. add it into a div
textContent = 'File name: ' + fileName;
$("#input-file-trigger").text(function () {
return $(this).text().replace("Select a file...", textContent);
});
}
/*
#### Drag & Drop Box ####
*/
.p_test{
display: inline-block;
}
.upl_drop{
border: 2px dashed #000;
margin: 0px 0px 15px 0px;
}
.btn--add p{
cursor: pointer;
}
.input-file-container {
position: relative;
width: auto;
}
.input-file-trigger {
display: block;
padding: 14px 45px;
background: #ffffff;
color: #1899cd;
font-size: 1em;
cursor: pointer;
}
.input-file {
position: absolute;
top: 0; left: 0;
width: 225px;
opacity: 0;
padding: 14px 0;
cursor: pointer;
}
.input-file:hover + .input-file-trigger,
.input-file:focus + .input-file-trigger,
.input-file-trigger:hover,
.input-file-trigger:focus {
background: #1899cd;
color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
<div class="file-input">
<div class="input-file-container upl_drop">
<input class="input-file" id="file-upload" type="file">
<label tabindex="0" for="file-upload" id="input-file-trigger" class="input-file-trigger">Select a file...</label>
</div>
<div id="file-upload-filename"></div>
</div>
<button class="btn--add" id="addtest">
Add
</button>
</div>
I'd advise against using incremental id attributes. They become a pain to maintain and also make the logic much more complicated than it needs to be.
The better alternative is to use common classes along with DOM traversal to relate the elements to each other, based on the one which raised any given event.
In your case, you can use closest() to get the parent .file-input container, then find() any element within that by its class. Something like this:
$(function() {
var $test = $('#p_test');
$('#addtest').on('click', function() {
var $lastGroup = $test.find('.file-input:last');
var $clone = $lastGroup.clone();
$clone.find('.input-file-trigger').text('Select a file...');
$clone.insertAfter($lastGroup);
});
$test.on('click', '.remtest', function(e) {
if ($('.file-input').length > 1)
$(this).closest('.file-input').remove();
}).on('change', '.input-file', function(e) {
if (!this.files)
return;
var $container = $(this).closest('.file-input');
$container.find(".input-file-trigger").text('File name: ' + this.files[0].name);
});
});
.p_test {
display: inline-block;
}
.upl_drop {
border: 2px dashed #000;
margin: 0px 0px 15px 0px;
}
.btn--add p {
cursor: pointer;
}
.input-file-container {
position: relative;
width: auto;
}
.input-file-trigger {
display: block;
padding: 14px 45px;
background: #ffffff;
color: #1899cd;
font-size: 1em;
cursor: pointer;
}
.input-file {
position: absolute;
top: 0;
left: 0;
width: 225px;
opacity: 0;
padding: 14px 0;
cursor: pointer;
}
.input-file:hover+.input-file-trigger,
.input-file:focus+.input-file-trigger,
.input-file-trigger:hover,
.input-file-trigger:focus {
background: #1899cd;
color: #ffffff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="p_test" id="p_test">
<div class="file-input">
<div class="input-file-container upl_drop">
<input class="input-file" type="file">
<label tabindex="0" for="file-upload" class="input-file-trigger">Select a file...</label>
</div>
<div class="file-upload-filename"></div>
</div>
<button class="btn--add" id="addtest">Add</button>
</div>
Note that I've made a couple of other optimisations to the code. Firstly it now makes a clone() of the last available .file-input container when the Add button is clicked. This is preferred over writing the HTML in the JS file as it keeps the two completely separate. For example, if you need to update the UI, you don't need to worry about updating the JS now, as long as the classes remain the same.
Also note that you were originally mixing plain JS and jQuery event handlers. It's best to use one or the other. As you've already included jQuery in the page, I used that as it makes the code easier to write and more succinct.
Finally, note that you didn't need to provide a function to text() as you're completely over-writing the existing value. Just providing the new string is fine.
I want to display a list of offers as styled radio buttons and I want them listed in a table with 3 offers in every row.
I style them in a css file like this:
input[type=radio].offer + label {
display:inline-block;
margin:-2px;
padding: 15px 12px;
margin-bottom: 0;
line-height: 20px;
width: 175px;
color: #333;
text-align: center;
text-shadow: 0 1px 1px rgba(255,255,255,0.75);
vertical-align: middle;
cursor: pointer;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);
border-bottom-color: #b3b3b3;
}
input[type=radio].offer:hover + label {
background-color: #f8ad51;
}
My asp page is like this:
sql1 = "SELECT * FROM company,prices WHERE companyId = company_id"
Set rs1 = Connect.Execute(sql1)
if NOT rs1.eof then
count = 1%>
<table border="0" cellpadding="2" cellspacing="10" width="600">
<tr>
<%
do until rs1.eof
price = a lot of calculations.......%>
<td><input type="radio" id="offer<%=count%>" name="offer" value="<%=rs1("companyId")%>" class="offer" onClick="this.form.submit1.disabled = !this.checked;">
<label for="offer<%=count%>"><%=rs1("navn")%><br><font size="1"><%=rs1("city")%></font><br><font size="4"><%=price%></font></label>
</td>
<%
if count MOD 3 = 0 then%>
</tr><tr>
<%end if
count = count + 1
rs1.MoveNext
loop%>
</tr>
</table>
<%end if%>
Everything is working fine and my list is looking ok, but now I want the list to be created in $(document).ready
So I now have this table:
<table id="tbl_result" border="0" cellpadding="0" cellspacing="0" width="380"></table>
I do a lot of javascript stuff and end up with this for every offer I find:
final_list+='<td>';
final_list+='<input type="radio" id="offer'+data.id+'" name="offer" value="'+data.id+' class="offer" onClick="this.form.submit1.disabled = !this.checked;">';
final_list+='<label for="offer'+data.id+'">'+data.cmp+'<br><font size="1">'+besked+'</font><br><font size="4">'+data.price+' kr.'+'</font></label>';
final_list+='</td>';
if ((finalList_count % 3) == 0) {
final_list+='</tr><tr>';
}
finalList_count = finalList_count + 1
And finally I add it to the page like this:
$("#tbl_result").html(final_list);
$("#tbl_result").show();
My problem is that my new offers dont get styled by my css file and I dont know how I can do that. Anybody that can help me out here?
You are missing a double quote " before you class attribute while creating final_list. So instead of value="'+data.id+' class="offer" you need to use value="'+data.id+'" class="offer" (note a " before class).
So the first line of final_list becomes:
final_list += '<input type="radio" id="offer' + data.id + '" name="offer" value="' + data.id + '" class="offer" onClick="this.form.submit1.disabled = !this.checked;">';
See this jsFiddle for a working example.