Trigger a link in adjoining table cell using jQuery - javascript

I have an HTML table where the first cell of each row contains text and the second cell contains a link. Like so:
<table id="foo">
<tr>
<td>Some text</td>
<td><img src="/path/to/image.jpg" /></td>
</tr>
...
</table>
I have a hover effect on the first table cell and when a user clicks on the first cell I wanted to trigger the link in the next cell so I chained a click function sort of like this:
$('#foo tr td:first-child').hover(...).click(
function() {
$(this).next().children().click();
}
);
This doesn't work. I've tried a few different ways with no success. I realise that I could put the text and link in the same cell and may end up doing that, but this is a simplified version of how my page actually is and it would take a bit of work to reshuffle things.
What am I doing wrong?

Clicking a link programmatically won't go to the href, only a native (user) click will, you can emulate it though, like this:
$('#foo tr td:first-child').hover(...).click(
function() {
window.location.href = $(this).next().children('a').attr('href');
}
);
This sets the window.location.href to go to the destination URL.

Related

Drag-and-Dropped Row's Parent Element is Null

I have two tables. The client should be able to drag and drop each table's row. They can either drop the row into a new position on the current table, or add the row to a specific position on the other table.
I'm able to insert the new row into the desired position on either table (and delete the original), but when I try to drag-and-drop that element a second time, it's parent element comes back as null, preventing me from repeating the drag-and-drop function a second time.
I need to solve this with pure, vanilla javascript.
//Table Structure (there are two of these, I'm trying to save space here)
<table id="thisTable" class="dropzone">
<thead>
...
</thead>
<tbody id="thisBody">
<tr id="123" class="drag-row draggable="true">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr id="124" class="drag-row draggable="true">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
//Collect Variables from the original row, the target table and the row being dropped into.
//Code to Insert New Row
function addRow(table_id, copy_id, target_id) {
let targetRow =document.getElementById(target_id);
let targetIndex = targetRow.rowIndex;
let copyRow = document.getElementById(copyId); // find row to copy
let targetTable = document.getElementById(tableID); // find table to append to
let clone = copyRow.cloneNode(true); // copy children too
//clone.id = "newID"; // change id or other attributes/contents
//table.appendChild(clone); // add new row to end of table
//targetTable.children[1].insertAdjacentElement("afterbegin", clone);
// Insert a row at desired index
let newRow = targetTable.insertRow(targetIndex);
newRow.id = clone.id;
newRow.draggable = "true";
newRow.classList.add("drag-row");
newRow.innerHTML = clone.innerHTML;
}
//Code to delete original row
dragSrcEl.remove();
I don't think I'll need to add in all the drag-and-drop code, because those seem to be working fine (when the parent element of the inserted node isn't null).
I've gone through a couple questions similar to this one and it seems that some commands in vanilla create new elements without tying them to the DOM? Supposedly by using the append() command you could add a new row to a table without disconnecting it from the DOM, but I tried that and got no result.
Which of the commands I'm using above would disconnect the newly inserted row from the DOM?
Is there a safe way, in vanilla js, to copy the contents of a row and inserted into a specific index of another (or the same table)?
It's frustrating because I finally got the code working... once.
Any thoughts?
Ah, I forgot to add the necessary event listeners to the newly inserted row, that would explain why drag-and-drop stopped working for them :)

insertBefore not updating rowIndex/nextSibling properties

It may sound stupid or even trivial for most experienced users, but I just landed a few hours ago on front-end javascript and I must say I am a bit puzzled with the behavior of the insertBefore javascript function.
My intention here is plain and simple: I have a table with its rows and cells, and in each row I have a cell with a button with the only purpose of duplicating that cell (with all its contents) and place the new duplicated cell right next to the original one.
I have a javascript function for it such like this one:
// id -> the id of the table I want the row to be added
// caller -> the object of the element that called the function
function duplicateRow(id, caller)
{
const table = document.getElementById(id);
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
table.insertBefore(clone, row.nextElementSibling);
}
This function is called like this (from an extract of my HTML):
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( 'competencies-table', this )"></button>
</td>
</tr>
So, what I would expect from it is that, at each click on the duplicate row button, it would create an exact copy of the row where the button is being clicked and add it right after that row.
My problem here is not with the duplicating (that is done just right and smooth as one would expect) but with where the new row is placed:
The first time, when there is only one row, it is placed at the end (since nextSibling is null).
The second time clicking the button on the first row (despite now having a sibling right after it), the new row is again placed at the end of the table (as if nextSibling for the first row was still null).
And so on (even strager placements happen when mixing duplications with the newly added rows).
Shouldn't the nextSibling and/or rowIndex properties be updated when adding a new node to the DOM? Is there a way of forcing them to update? What is it that I have wrong? My code, my understanding of how it should work?
I am surely open to any possible explanation/solution/alternative to achieve what I need, and thank you all in advance!
The problem is that initial table row is wrapped in a tbody element (for which you can omit both start and end tag), which is required according to the content model of tables. However, when you programmatically add more rows, they are inserted outside the tbody and your initial row is the only child of that implicit tbody, so the DOM tree looks like this:
<table>
<tbody>
<tr></tr>
</tbody>
<tr></tr>
<tr></tr>
</table>
To solve it I suggest to add a clone to cloned row's parent:
function duplicateRow(caller){
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
row.parentNode.insertBefore(clone, row.nextElementSibling);
}
<table id="competencies-table">
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( this )">Duplicate</button>
</td>
</tr>
</table>

How to get Id value by clicking Edit button on the same row of DataTable

I use jQuery Datatable for listing records and add an Action button (Edit) for editing the record on a modal dialog. If I select a row I can get the row id value and open the related record on modal dialog. However, if I click the Edit button directly, I cannot get the Id value of the related record (on the same row) because it is not selected first when clicking Edit button. What I want to do is that: I want to get the Id value of the row on which I click the Edit button. Is it possible? If not, can I select the hovered row programmatically when I click the Edit button? (If the prior scenario is possible I would prefer it). Any idea?
function openModal() {
var table = $('#dtbListAccount').DataTable();
var oRow = $('this').parents('tr')[0];
var oData = table.fnGetData(oRow);
//code omitted for brevity
};
You can use this code to achieve this.
var table;
$(document).ready( function () {
table = $('#example').DataTable();
} );
$('body').on('click', '#btnEdit', function(){
//to get currently clicked row object
var row = $(this).parents('tr')[0];
//for row data
console.log( table.row( row ).data() );
});
It will return row data as a string array.
Live Demo Here
Use the browser console to see the results.
Here's the full source code. Hope this helps :)
//when button (edit button here) is clicked.... Note: no need id for buttons too, just use <button> tag
$('table button').click(function() {
var tr = $(this).closest('tr');
var id = tr.children('td:eq(0)').text(); //get the text from first col of current row
console.log(id); //you'll get the actual ids here
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Surname</th>
<th>Action</th>
</tr>
<tr>
<td>1</td>
<td>Hans</td>
<td>Jahnsen</td>
<td>
<button>Edit</button>
</td>
</tr>
<tr>
<td>2</td>
<td>Robert</td>
<td>Boylstat</td>
<td>
<button>Edit</button>
</td>
</tr>
<tr>
<td>3</td>
<td>Jim</td>
<td>Alexi</td>
<td>
<button>Edit</button>
</td>
</tr>
</table>
Assign the row-id(s) to the edit buttons as well, write click events for the edit buttons which, based on the id of the button clicked on, triggers the edit functionality / view.
You could assign the row-id(s) to the buttons either when rendering itself, or write a small function that does the same on page load.
If the id is on the parent container then find it's value and use it. If it's a sibling then do the same.

hide css class delete_icon with reference/using button value

template.html
<td style="width:150px;"><input type="button" name="delete" value="{{ report_person.report_person.id}}" class="delete_icon" />{{report_person.report_person.name }}
</td>
The above row is created dynamically using python/django.Its the person name and delete icon.
For every row created dynamically,the delete icon will appear.
<td class="ir-shade" colspan="4"><button id="{{report_person.report_person.id}}" type="button" class="add_treatment" class="button_style">Add treatment notes</button>
<div id="result-{{report_person.report_person.id}}" class="toggle"></div></td>
Add treatment notes is a toggle button,it show the treatment details on one toggle,at this state the name of button become Hide treatment notes.
I want to hide the delete_icon class for individual name when the treatment note is open f.Since i am using comman css class for delete_icon,using this $('.delete_icon').hide(); is hiding the delete icon for all rows created dynamically.Only unique thing is the value of that delete button,that means the delete_icon.Is any possibility is their to hide the css class icon with reference to value.So that the delete image will hide only for cases the treatment notes are in open condition.
Note:It is some django code are used for passing variable,looks different from html.
In jQuery, you can use this to find child elements. Assuming that this is all in one div, or span, or some sort of container. You could simply search downwards from that container and toggle off your delete button.
$(this).children("input.delete_icon").hide();
Going off of your supplied template, you would use children and also .prev() to navigate up your DOM, and then back down.
Your code will roughly be:
$(this).closest('tr').prev().children(":first").children('input.delete_icon').hide();
Let's walk through this:
$(this) is your currently 'clicked' table row, that includes your
add_treatment button.
.closest('tr') looks UP for the nearest
table row. It will find the table row that your button is placed
inside of
.prev() looks for the previous sibling of the selected
object. Since we are currently selecting the <tr> your button is in
(as per our .closest('tr')) it will look for the previous <tr>
which is above it, and holds your delete_icon button.
Now, we are on the <tr> of your button object, but we need to navigate downwards
.children(":first") will navigate to the first child of the current element. This would be your first <td> element.
We need to navigate down one more, so we select the button child by using .children('index.delete_icon')
You have now "selected" your .delete_icon button, and can .hide() it.
Try using the jQuery .find() function.
$('.add_treatment').on('click', function() {
var $this = $(this);
$this.find('input[class*='delete_icon']').prop('disabled', true);
// toggle notes
// hide delete icon (if you do not like the css below)
});
Adding css rules would help reduce the amount of javascript functions/actions needed.
tr td .delete_icon {
display:none;
}
tr:hover td .delete_icon {
display:block;
}
There are other ways to do this that are more efficient but we would need more html structure.
if the delete icon is in the following td (created dynamically) you need:
$('.add_treatment').click(function(){
// should point to the parent td, the next td, its children,
// and hide the first child which is the input button
$(this).parent().next().children('.delete_icon').hide();
});

Why does new jQuery on('change') not fire on iOS, while it works on Android?

I'm trying to update a piece of code to the new jQuery on() method. I have a table and a select box. I want to populate the select box with one option for each table header cell, which works fine.
Then I want to toggle header cells depending on options selected. This works on desktop and Android but not on iPad.
This is what I'm doing:
$('table th').each(function(i) {
// add option to select box for each table column
var toggle = $('<option value="'+id+'">'+th.text()+'</option>');
$('select#toggle').append(toggle); }
// set up options
$('select#toggle').one("updateCheck", function(){
console.log("th-setup");
// if condition, then select, otherwise don't select
}).trigger("updateCheck");
});
// listen for select changes
$('select#toggle').on('change', function() {
console.log("change detected");
})
HTML
<select id="toggle"></select>
<table class="sample">
<thead>
<tr>
<th>one</th>
<th>two</th>
<th>three</th>
<th>four</th>
</tr>
</thead>
</table>
I first tried to bind the following code to document like so:
$(document).on('change',$('select#toggle'), function(...)
but this simply crashes my iPad before the page is displayed.
Can someone tell me if I'm doing something wrong and why selecting an option does not trigger a change event on iPad vs triggering one on Android and desktop?
It should be:
$('body').on('change', '#toggle', function() { ... });
The first argument is the list of event names. The second is a selector string (not a jQuery object). The third is the handler function.
There's no need to qualify an identifier reference in a selector with the node type.

Categories