I have a couple of td elements I would like to make editable and then when a button is clicked have them revert to static. The problem is getting these elements to toggle back and forth. How do I accomplish this in the easiest way possible?
It seems recursive where I would click edit -> replace the elements with input elements -> give button static function handler -> click button -> replace the input with static content -> add event handler. The only issue I have is attaching the handler to the newly created elements.
Javascript:
// how do I encapsulate this into its own function? Seems recursive.
$('.edit-me').click(function(){
// remove the other editable fields.
// For some reason the second toggle errors out. - Why?
makeEditable(this);
$(this).toggleClass('edit-me');
$(this).toggleClass('static-me');
});
// attach a handler to the newly created elements
$('.static-me').click(function(){
// this is not working because newly created items do not have the event associated to them.
console.log("HERE");
makeStatic(this);
$(this).toggleClass('edit-me');
$(this).toggleClass('static-me');
});
}
$('.delete-me').click(function(){
pair_delete(this);
});
function makeEditable(obj){
// replace the elements with an editable element.
$(obj).parent().children('td.editable').each(function(index, item){
$(item).html('<input class="form-control edit-mode editing" data-field="' + $(this).attr('data-field') + '" value="' + $(this).html() + '" >');
// toggle for event handling.
$(item).removeClass('editable');
$(item).addClass('edit-mode');
});
$('.editing').change(function(){
// create the update
pair_update(this);
});
}
function makeStatic(obj){
// makes the row static.
$(obj).parent().children('td.edit-mode').each(function(index, item){
// replace with input field.
$(item).html('<td class="editable" data-field="'+ $(item).attr('data-field') +'">' + $(item).find('.editing').val() + '</td>');
$(item).addClass('editable');
$(item).removeClass('edit-mode');
});
}
HTML:
<td class="editable" data-field="pair_name"><?=$pair['pair_name']?></td>
<td class="editable" data-field="email_name"><?=$pair['email_id']?></td>
<td class="editable" data-field="sent_event_id"><?=$pair['sent_event_id']?></td>
<td class="editable" data-field="minutes"><?=$pair['minutes']?></td>
<td class="edit-me">
<button type="submit" class="btn btn-primary">
<i class="glyphicon glyphicon-pencil icon-white"></i>
</button>
</td>
if you'd rather keep to toggling, use .on('click', '.classname', function() { // do stuff here; }); instead of .click() (it should solve the bindings to the new elements on the dom)
another solution is to try something without toggle, since toggle gets messy with switching out elements and such. something more straight forward like this could work : http://jsfiddle.net/swm53ran/177/
$(document).ready(function() {
$('table').on('click', '.edit', function() {
var name = $(this).closest('tr').find('.name').html();
var email = $(this).closest('tr').find('.email').html();
$(this).closest('tr').find('.name').html('<input type="text" value="'+ name +'"/>');
$(this).closest('tr').find('.email').html('<input type="text" value="'+ email +'"/>');
$(this).closest('tr').find('.edit').closest('td').html('Save');
});
$('table').on('click', '.save', function() {
var name = $(this).closest('tr').find('.name').find('input').val();
var email = $(this).closest('tr').find('.email').find('input').val();
$(this).closest('tr').find('.name').html(name);
$(this).closest('tr').find('.email').html(email);
$(this).closest('tr').find('.save').closest('td').html('Edit');
});
});
<table>
<tr>
<td class="name">Name1</td>
<td class="email">Email1</td>
<td class="action">Edit</td>
</tr>
<tr>
<td class="name">Name2</td>
<td class="email">Email2</td>
<td class="action">Edit</td>
</tr>
<tr>
<td class="name">Name3</td>
<td class="email">Emaiil3</td>
<td class="action">Edit</td>
</tr>
</table>
Related
I have a table (generated at run time) and a dropdown. Before the page is up, the table does not exist. Once the page is up, the table and the dropdown look similar to this fiddle:
$("#aDropDown").val($("WhatToPutHere?").text());
I am trying to do the following: when the user select the Select button, I need the dropdown selected item to match the Type for the row.
Any example would be appreciated.
Thanks
Mike
first, you have multiple button with the same id. They should share a class.
<button class="aButton">Select</button>
next, what you want is the .text() of the prev <td>
$('.aButton').on('click', function() {
var type = $(this).closest('td').prev('td').text();
$("#aDropDown").val(type);
});
JSFIDDLE
as #Mackan pointed out, you might encounter a problem if you're not using a
delegated event, as you're creating the table dynamically. in that case the following code would work better :
$(body).on( 'click', '.aButton' , function() {
var type = $(this).closest('td').prev('td').text();
$("#aDropDown").val(type);
});
$('.aButton').click(function(e) {
var btn = $(e.target);
$("#aDropDown").val(btn.data('category'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<thead>
<td>item</td>
<td>Type</td>
<td>Action</td>
</thead>
<tr>
<td class="anItem">Apple</td>
<td class="aType">Fruit</td>
<td>
<button class="aButton" data-category="Fruit">Select</button>
</td>
</tr>
<tr>
<td class="anItem">Orange</td>
<td class="aType">Fruit</td>
<td>
<button class="aButton" data-category="Fruit">Select</button>
</td>
</tr>
<tr>
<td class="anItem">collard</td>
<td class="aType">Veggie</td>
<td>
<button class="aButton" data-category="Veggie">Select</button>
</td>
</tr>
</table>
<br/>
<br/>aDropDown:
<select id="aDropDown">
<option value="Fruit">Fruit</option>
<option value="Veggie">Veggie</option>
</select>
Removed id attribute from each button (id should be use for unique elements).
Added aButton class to each button.
As I said above, your code must run on some event. Here's how I'd do it with minimal markup changes (just a class on the buttons):
$('.selectBtn').click(function() {
var myVal = $(this).closest('tr').find('.aType').text();
console.log(myVal);
$("#aDropDown").val(myVal);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<thead>
<td>item</td>
<td>Type</td>
<td>Action</td>
</thead>
<tr>
<td class="anItem">Apple</td>
<td class="aType">Fruit</td>
<td>
<button id="aButton" class="selectBtn">Select</button>
</td>
</tr>
<tr>
<td class="anItem">Orange</td>
<td class="aType">Fruit</td>
<td>
<button id="aButton" class="selectBtn">Select</button>
</td>
</tr>
<tr>
<td class="anItem">collard</td>
<td class="aType">Veggie</td>
<td>
<button id="aButton" class="selectBtn">Select</button>
</td>
</tr>
</table>
<br/>
<br/>aDropDown:
<select id="aDropDown">
<option value="Fruit">Fruit</option>
<option value="Veggie">Veggie</option>
</select>
Since you said your table is created dynamically, you should use a bind to a persistent element, using a delegated event (like document or body):
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
All of the other answers (time of writing) uses binds that will add the event handlers to the actual buttons. This means that if the button doesn't exist, or gets temporarily removed, the bind will be lost or can't be created to start with.
Using a delegated event:
$(document).on('click', '.aButton', function() {
or..
$('body').on('click', '.aButton', function() {
Also notice that the above binds use a class selector, .aButton, because id's must be unique (and yours were not).
<button class="aButton">Select</button>
Full example at jsFiddle:
$(document).on('click', '.aButton', function() {
$("#aDropDown").val($(this).parent().prev('td').text());
});
Edit:
If the table structure is in danger of changing (adding more td's or tr's), the below script will be better at finding the correct td (by class .aType):
$(document).on('click', '.aButton', function() {
$("#aDropDown").val($(this).closest('tr').find('.aType').text());
});
Suppose onclick handler is set for a <tr> is it possible to disable/overwrite it for one particular <td>?
<tr onclick='somefunction()'>
<td> </td> <!--onclick should work here-->
...
<td> </td> <!--onclick should not work here-->
...
<td> </td> <!--onclick should work here-->
</tr>
Of course I can set it for each <td> separately or pass the name of a td to the function and decide what to do based on this name, but it seems like there should be a simpler solution.
I found the easiest was to stop the event being passed to the parent html using onclick=event.stopPropagation() in the <td> tag.
So <td class=whatever onclick=event.stopPropagation()>cell data<td>
In somefunction you could check the cellIndex of the td, and return early, if a non-clickable cell has been clicked. Something like this:
function somefunction (e) {
if (e.target.cellIndex === 1) {return;}
/* Do something, the td is clickable */
}
To get this work with an inline handler, you've to pass the event object:
<tr onclick='somefunction(event)'>
A live demo at jsFiddle.
Things will get a bit more complex, if you've elements within cells. In that case you have to find a td parent element, like so:
function somefunction (e) {
var target = e.target; // Cache the target element
while (target) { // Iterate through elements
if (target.tagName === 'TD') { // TD found, stop iteration
break;
}
target = target.parentElement; // Set target as a parent element of the current element
}
if (target === null) {return;} // Check that we really have a TD
if (target.cellIndex === 1) {return;} // A non-clickable cell clicked
:
}
A live demo at jsFiddle.
Edit 2018
In 2018 elements have closest() method, hence the loop above is not needed, target = e.target.closest('td') will make sure a td is used.
A very simple way would be to use CSS pointer-events: none, but unfortunately this doesn't work in FF in this particular case in IE<11 at all, though works well in Chrome and IE11. Also preventing pointer events would be bad, if the cell happens to contain interactive elements.
A live demo at jsFiddle.
EDIT:-
Try something like this.
HTML:
<tr id="row">
<td> </td> <!--onclick should work here-->
...
<td class="noChange"> </td> <!--onclick should not work here-->
...
<td> </td> <!--onclick should work here-->
</tr>
JavaScript:
window.onload = function() {
var row = document.getElementById("row");
for (i=0; i<row.childNodes.length; i++) {
if (row.childNodes[i].class != "noChange") {
row.childNodes[i].onclick="doStuff()";
}
}
}
<html>
<body>
<table border="1">
<tr onclick='somefunction(this)'>
<td><input type="text"></td> <!--onclick should work here--> ...
<td><input type="text"></td> <!--onclick should not work here--> ...
<td><input type="text"></td> <!--onclick should work here-->
</tr>
</table>
<script>
function somefunction(element) {
var td = element.children;
console.log(td);
var inputObj = td[1].children;
console.log(inputObj[0]);
inputObj[0].disabled = true;
}
</script>
</body>
</html>
The children property 'element.children' returns a list of an element's child elements, as an HTMLCollection object.
enjoy :)
I've got a jQuery/AJAX solution set up to update and delete items that are displayed in a table. The AJAX part works fine but once an item is deleted I need to be able to remove it from view and I can't figure out how to identify the selected item(s) based on their value after the submit button is clicked.
Here's my jQuery:
$('#button').click(function(event){
var order = $("#sortable tbody").sortable("serialize");
order += "&" + $("form[name=favorites]").serialize().replace(/%5B%5D/g, '[]');
order += "&crudtype=update_favorites";
$('#savemessage').html('<p>Saving changes...</p>');
$.post("/crud",order,function(theResponse){
$('#savemessage').html(theResponse);
});
});
});
My HTML is generated from PHP so the quantities and IDs are variable but the format is as follows:
<tr class="odd" id="field_37">
<td class="handle">Item #1 Name</td>
<td><input type="checkbox" name="fid[]" id="fid" value="37" class="box check-child"></td>
</tr>
<tr class="even" id="field_29">
<td class="handle">Item #2 Name</td>
<td><input type="checkbox" name="fid[]" id="fid" value="29" class="box check-child"></td>
</tr>
So effectively what (I think) I need is to add to my .click function something like "foreach checked fid, remove the corresponding row ID" if that makes any sense.
A basic selector to get a checked checkbox is
'input[type="checkbox"]:checked'
or
'input:checkbox:checked'
Now you can either use has() or loop through and use closest to get the trs
$('input[type="checkbox"]:checked').closest("tr").remove();
or
$('tr:has(input[type="checkbox"]:checked)').remove();
You can do it like this: http://jsfiddle.net/dSANw/
When user clicks on checked box add class to the parent tr
$(".box").click(function() {
if($(this).is(':checked')) {
$(this).parents('tr').addClass('checkedtd');
} else {
$(this).parents('tr').removeClass('checkedtd');
}
});
When clicked on delete, get all tables tr's classed 'checkedtd' and delete
$("#delt").click(function() {
alert($('.checkedtd').length);
$('.checkedtd').remove();
});
I used a code which I got in the net that adds a table row every onclick event. It worked perfect for me until I realized I needed to have an onclick event for every row that when clicked, it will delete the row.
Is there a way for that to happen using my code?
Please see codes below:
Javascript/JQuery code:
<script>
var counter = 2;
function addRow() {
event.preventDefault();
var newRow = jQuery('<tr><td><label>'+ counter +'</label></td><td><textarea name="txtActionStep' + counter + '" style="width:300px; height: 50px; word-wrap:break-word;"></textarea></td><td valign="top"><input type="text" name="txtOwner' + counter + '"/></td></tr>');
counter++;
jQuery('table.actionsteps-list').append( newRow );
}
</script>
HTML Code:
<table class="actionsteps-list" width="510">
<tr>
<th colspan="3" align="left">Action Steps</th>
</tr>
<tr>
<td>Step #</td><td>Action Step</td><td>Owner</td>
</tr>
<tr>
<td><label>1</label></td>
<td><textarea name="txtActionStep1" style="width:300px; height: 50px; word-wrap:break-word;"></textarea></td>
<td valign="top"><input type="text" name="txtOwner1" /></td>
</tr>
</table>
<table width="510">
<tr>
<td align="right">Add Action</td>
</tr>
</table>
Thank you!
Sure, using delegation we can accomplish that.
$('table.actionsteps-list').on('click', 'tr', function(e){
$(this).remove();
});
You probably want to add a button to your row to signal a deletion, so let's assume you add (to each row):
<td><button class="delete">Delete</button></td>
Then just change your delegation method like this:
$('table.actionsteps-list').on('click', '.delete', function(e){
e.preventDefault(); // stops the page jumping to the top
$(this).closest('tr').remove();
});
Use a delegate ot catch the event at the table level, that way any new row that you add will also be handled:
$('.actionsteps-list').on('click', 'tr', function(){
$(this).remove();
});
Side note:
Don't use the javascript: protocol for inline Javascript, that's only used when you put Javascript in the href attribute of a link:
Add Action
I have a table with some td elements with the same class. But i want to change claas only in specified area .selected.
I make this:
<tr>
<td>--</td>
<td class="mcost"></td>
</tr>
<tr class="selected">
<td><input type="radio" name="work" onclick="selone('.selected','#sel1','m');" checked="checked" /></td>
<td id="sel1" class="mcost"></td>
</tr>
<tr class="selected">
<td><input type="radio" name="work" onclick="selone('.selected','#sel2','m');" /></td>
<td id="sel2" class="mcost"></td>
</tr>
<tr>
<td>--</td>
<td class="mcost"></td>
</tr>
And try this:
function selone(g,f,n){
$(g).each(function(){
$('.'+n+'cost').removeClass().addClass(n+'cost_dis');
});
$(f).removeClass().addClass(n+'cost');
}
But it's change class to all mcost elements =( Not only in specified area. How to improve it?
in your .each, you requery the entire dom for .mcost cells. You need to do the following:
function selone(g,f,n){
$(g).each(function(){
$(this).find('.'+n+'cost').removeClass().addClass(n+'cost_dis');
});
$(f).removeClass().addClass(n+'cost');
}
This will only change the .mcost items inside of your $(g) element. This should work. Let me know.
$(".selected td").each( function() { $(this).addClass("whatever"); } );
You could use a better, and unobtrusive approach:
remove the onclick-events in the html.
Use the script to this:
$(function () {
$(".selected :radio").click(function () {
var selected = $(this).parents(".selected:first");
selected.removeClass().addClass('mcost_dis');
selected.find('.mcost').removeClass().addClass('mcost');
});
});
(However the last line doesn't add much)
The problem is in the part $('.'+n+'cost')
this does not consider the context it is called in. It will allways select all .mcost elements in the page.
if you change it to:
$(this).find('.'+n+'cost')
it will search only in the context of g. Do the same for f.
However, it all depends on what g and f are. Using .selected in this case it should give you what you want.
I don't think you need the each loop at all if you change your selector a bit. Wouldn't this work:
function selone(g,f,n) {
// find elements that already have the class, and change them
$(g + " ." + n + 'cost').removeClass().addClass(n+'cost_dis');
// add class to newly selected item
$(f).removeClass().addClass(n + 'cost');
}
Assuming you call it with
selone('.selected','#sel1','m');
Then within the function $(g + " ." + n + 'cost') evaluates as $(".selected .mcost") - which means "find elements of class 'mcost' that are descendents of elements with class 'selected'".