Find out next <td> id by clicking on a <td> - javascript

I have a some td's like this
<td id="first">first</td>
<td id="second">second</td>
<td id="third">third</td>
<td id="fourth">fourth</td>
By clicking on one td I want to find out the id's of its next two tds.
When I click on 'first' I need to get 'second' and 'third' values.
How can I achieve this using JQuery or Javascript?

jQuery
This will alert 'undefined' if you click on the last cell.
jsFiddle
$('td').click(function () {
alert($(this).next().attr('id'));
});
JavaScript
This method is a bit hacky because .nextSibling returns a text node object (not a node) which doesn't contain an id property. It gets a list of siblings from its parentNode, we then iterate over them until we get a match, the next index is the next sibling.
jsFiddle
var tds = document.getElementsByTagName('td');
for (var i = 0; i < tds.length; i++) {
tds[i].onclick = function () {
var siblings = this.parentNode.children;
var i = 0;
while (siblings[i] != this) { i++; }
if (i < siblings.length)
alert(siblings[i + 1].id);
};
}

JQuery has sibling selectors and functions that you can use for this:
http://api.jquery.com/next-siblings-selector/
http://api.jquery.com/siblings/

$('td').click(function(e) {
e.preventDefault();
var id = $(this).next().attr('id');
if (id) alert(id);
});

You can use next() twice. You will have to check if next has brought an element or not as for second last you wont get second and for last you wont get first and second next element.
Live Demo
$('td').click(function () {
first = $(this).next('td');
second = first.next('td');
if (first.length) alert(first.text() );
else alert("No next");
if (second.length) alert(second.text());
else alert("No next");
});

Related

Apply CSS on matching text

I am trying to apply a class where text matches with sibling elements.
My certain condition is:
I have a table with multiple rows based on data that I get through database.
One of the td elements has my defined class.
Now I wanted to apply a class only on those td elements where the text of this element matches with another one.
So It would be like, td's whose html/text is equal has that class.
I tried:
$('#table tbody>tr').find('td[class^="customtd"]').each(function() {
if($(this).html().trim() == $(this).siblings('td').html().trim()) {
$(this).addClass('active');
}else {
$(this).removeClass('active');
}
});
You'd have to iterate each sibling td (or use filter), check for a text match, then add the class:
$('#table tbody>tr').find('td[class^="customtd"]').each(function() {
var text = $(this).text();
$(this).siblings("td").filter(function() {
return $(this).text() == text;
}).addClass("active");
});
You have to set the value you are searching for and then loop through all table data. If you find a match, add the certain class.
Furthermore you should cache variables in jQuery and avoid using each() function since its performance is really bad compared to for loops.
//cache the element you are searching for
var search = $('.customtd').html().trim();
//cache the whole table so we can use a for loop
var table = $('#table tbody>tr>td');
//use for loop for more performance
for (var i = 0; i < table.length; i++) {
if(table.eq(i).html().trim() == search) {
table.eq(i).addClass('active');
}else {
table.eq(i).removeClass('active');
}
}
Here is a working example:
http://jsfiddle.net/jnh2heuh/2/

How to remove all classes except the one you clicked?

This functions starts when I click on a link. It needs to remove all '.is-active' classes on the elements with the attribute [data-route]. And add the class '.is-active' on the [data-route] element that is connected with the link I clicked on.
toggle: function(section){
var sections = document.querySelectorAll('[data-route]');
for (i = 0; i < sections.length; i++){
document.querySelector('[data-route]').classList.remove('is-active');
}
document.querySelector(section).classList.add('is-active');
}
But this doesn't work. It doesn't remove the classes?
See example: http://jordypouw.github.io/myFED2/deeltoets1/index.html
P.S. it has to be in vanilla JavaScript.
toggle: function(section){
var sections = document.querySelectorAll('[data-route]');
for (i = 0; i < sections.length; i++){
sections[i].classList.remove('is-active');
// querySelectorAll return an array of dom elements, u can access them directly.
}
// I suppose in your case that ' section ' variable is the clicked element so :
section.classList.add('is-active')
// if not you have to store the dom element from the event, and add the class here.
}
you can do this:
for (var item of document.querySelectorAll('[data-route]')) {
item.classList.remove('is-active');
}
This is ecmascript6 so it won't work on old browsers. I like it because it's clean and nice. to get it to work on other browsers you must convert the nodes collection into a real array, so you could loop it.
toggle: function(section){
document.querySelectorAll("[data-route]").forEach( e => {
e.classList.remove("is-active");
});
// querySelectorAll return an array of dom elements, u can access them directly.
// I suppose in your case that ' section ' variable is the clicked element so :
document.querySelectorAll("[data-route]").forEach( e => {
e.classList.add("is-active");
});
// if not you have to store the dom element from the event, and add the class here.
}
Set up a variable for the clicked item..
jQuery('.clicker-item').on("click", function(){
var clicked = jQuery('.clicker-item').not(jQuery(this));
clicked.removeClass("active")
jQuery(this).toggleClass("active")
});
I felt, that other answers were not neat enough.
toggle: (s) => {
// Find all other sections and remove the active class:
document.body.querySelectorAll('[data-route]').forEach(i => i.classList.remove('is-active'))
// Add active to the inputted section:
s.classList.add('is-active')
}
shouldn't it be this:
toggle: function(section){
var sections = document.querySelectorAll('[data-route]');
for (i = 0; i < sections.length; i++){
document.querySelector('[data-route]').removeClass('is-active');
}
document.querySelector(section).addClass('is-active');
}
Edit: Sorry, I should have said removeClass and addClass

How to reduce 180 lines of code down to 20 in Javascript?

I have a lot of click handler functions which are almost (textually and functionally) identical. I've got a menu with maybe 10 items in it; when I click on an item, the click handler simply makes one div visible, and the other 9 div's hidden. Maintaining this is difficult, and I just know there's got to be a smart and/or incomprehensible way to reduce code bloat here. Any ideas how? jQuery is Ok. The code at the moment is:
// repeat this function 10 times, once for each menu item
$(function() {
$('#menuItem0').click(function(e) {
// set 9 divs hidden, 1 visble
setItem1DivVisible(false);
// ...repeat for 2 through 9, and then
setItem0DivVisible(true);
});
});
// repeat this function 10 times, once for each div
function setItem0DivVisible(on) {
var ele = document.getElementById("Item0Div");
ele.style.display = on? "block" : "none";
}
Create 10 div with a class for marking
<div id="id1" class="Testing">....</div>
<div id="id2" class="Testing">....</div>
<div id="id3" class="Testing">....</div>
and apply the code
$('.Testing').each(function() {
$(this).click(function() {
$('.Testing').css('display', 'none');
$(this).css('display', 'block');
}
}
$(document).ready(function (){
$("div").click(function(){
// I am using background-color here, because if I use display:none; I won't
// be able to show the effect; they will all disappear
$(this).css("background-color","red");
$(this).siblings().css("background-color", "none");
});
});
Use .siblings() and it makes everything easy. Use it for your menu items with appropriate IDs. This works without any for loops or extra classes/markup in your code. And will work even if you add more divs.
Demo
Fiddle - http://jsfiddle.net/9XSJW/1/
It's hard to know without an example of the html. Assuming that there is no way to traverse from the menuItem to ItemDiv - you could use .index and .eq to match up the elements based on the order they match with the selector.
var $menuItems = $("#menuItem0, #menuItem1, #menuItem2, ...");
var $divs = $("#Item0Div, #Item1Div, #Item2Div, ...");
$menuItems.click(function(){
var idx = $(this).index();
// hide all the divs
$divs.hide()
// show the one matching the index
.eq(idx).show();
})
Try
function addClick(i) {
$('#menuItem'+i).click(function(e) {
// set nine divs hidden, 1 visble
for( var j = 0; j < 10; ++j ) {
var ele = document.getElementById("Item"+j+"Div");
ele.style.display = (i == j ? "block" : "none");
}
});
}
// One click function for all menuItem/n/ elements
$('[id^="menuItem"]').on('click', function() {
var id = this.id; // Get the ID of the clicked element
$('[id^="Item"][id$="Div"]').hide(); // Hide all Item/n/Div elements
$('#Item' + id + 'Div').show(); // Show Item/n/Div related to clicked element
});
Obviously this would be much more logical if you were using classes instead:
<elem class="menuItem" data-rel="ItemDiv-1">...</elem>
...
<elem class="ItemDiv" id="ItemDiv-1">...</elem>
$('.menuItem').on('click', function() {
var rel = $(this).data('rel'); // Get related ItemDiv ID
$('.ItemDiv').hide(); // Hide all ItemDiv elements
$('#' + rel).show(); // Show ItemDiv related to clicked element
});
Save the relevant Id's in an array - ["Item0Div", "Item1Div", ...]
Create a generic setItemDivVisible method:
function setItemDivVisible(visible, id) {
var ele = document.getElementById(id);
ele.style.display = visible ? "block" : "none";
}
And set your click handler method to be:
function(e) {
var arrayLength = myStringArray.length;
for (var i = 0; i < idsArray.length; i++) {
setItemDivVisible(idsArray[i] === this.id, idsArray[i]);
}
}
I think this will do the trick

jQuery - Identify td that was clicked

I have created a grid (table) inside of an HTML doc based on user preference.
After appending the rows and cells to the table, it appears jQuery cannot access them properly? Its as if it doesn't consider them part of the DOM.
Essentially I will be handling everything inside of a multidimensional array for my data, such as:
[ ["","",""], ["","",""], ["","",""] ]
It is currently set up with appended rows named #row_0, #row_1, etc and td's added without an ID.
I need to be able to map the clicked TD to my jQuery so I can perform some functions and logic on it.
Is there any way to respond to my JS when a user clicks on a td with something like:
tr = [1]
td = [2]
Current code found here:
http://jsfiddle.net/HNjMR/4/
Here live example friend: http://jsfiddle.net/HNjMR/5/
$("#board").on('click', 'td', function(){
$("#board td").css("background","white");
$(this).css("background","red");
});
This happens because when you insert a new dynamic element on the page, ready jquery can not find it. The ready by default runs events only on existing elements at load time. While some functions like .on, solve this problem.
http://jsfiddle.net/898n2/1/
$( document ).on( "click", "td", function() {
alert(this.id);
});
This is using your current code
Notice I have used .on - this is a 'live' event using jquery
I have also modified your naming of each td to read col_1_2 - with 1 being col and 2 being row.
$("#row_"+i).append("<td id='col_"+j+"_"+i+"'> </td>");
when the td element click handler is added those target elements are not yet created so use event delegation and bind the click handler to the #board element with the target element selector as td as given below. Then use .index() to find the position of row and td(Add 1 to index as it is 0 based)
$("#board").on('click', 'td', function () {
var $td = $(this);
var td = $td.index() + 1,
row = $td.parent().index() + 1;
alert(td + '-' + row);
});
Demo: Fiddle
Also your create code is buggy, jQuery append does not work like string concatenation
$('#board tr').remove();
var size = parseInt($("#size").val());
for (var i = 0; i < size; i++) {
var $tr = $('<tr />', {
id: 'row_' + i
}).data('index', i + 1);
$("#board").append("<tr id='row_" + i + "'>");
for (var j = 0; j < size; j++) {
$('<td />', {
id: 'col_' + j
}).data('index', j + 1).appendTo($tr);
}
$("#board").append($tr);
}
Demo: Fiddle
Here the solution, remember that you should bind the eventclick when the elements was created, you souldn't use a generical $('td').click event, this does not work because some elements was not added in DOM yet.
$("#go").click(function(){
var size = $("#size").val();
if(size<3 || size>10){
alert("That is not a valid input. Please select 3-10");
return;
}
$('#board tr').remove();
// Check this bellow
var size = parseInt($("#size").val()),
tr = $("<tr/>"), td = $("<td/>"), tdc;
for(var i=0;i < size;i++){
for(var j=0;j<size;j++){
tdc = td.clone();
tdc.attr('id', 'col_'+j);
tr.append(tdc);
tdc.click(function(){
alert(this);
});
}
tr.attr('id','row_'+i);
$("#board").append(tr);
}
});

AJAX: Applying effect to CSS class

I have a snippet of code that applies a highlighting effect to list items in a menu (due to the fact that the menu items are just POST), to give users feedback. I have created a second step to the menu and would like to apply it to any element with a class of .highlight. Can't get it to work though, here's my current code:
[deleted old code]
The obvious work-around is to create a new id (say, '#highlighter2) and just copy and paste the code. But I'm curious if there's a more efficient way to apply the effect to a class instead of ID?
UPDATE (here is my updated code):
The script above DOES work on the first ul. The second ul, which appears via jquery (perhaps that's the issue, it's initially set to hidden). Here's relevant HTML (sort of a lot to understand, but note the hidden second div. I think this might be the culprit. Like I said, first list works flawlessly, highlights and all. But the second list does nothing.)?
//Do something when the DOM is ready:
<script type="text/javascript">
$(document).ready(function() {
$('#foo li, #foo2 li').click(function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
//When a link in div is clicked, do something:
$('#selectCompany a').click(function() {
//Fade in second box:
//Get id from clicked link:
var id = $(this).attr('id');
$.ajax({
type: 'POST',
url: 'getFileInfo.php',
data: {'id': id},
success: function(msg){
//everything echoed in your PHP-File will be in the 'msg' variable:
$('#selectCompanyUser').html(msg)
$('#selectCompanyUser').fadeIn(400);
}
});
});
});
</script>
<div id="selectCompany" class="panelNormal">
<ul id="foo">
<?
// see if any rows were returned
if (mysql_num_rows($membersresult) > 0) {
// yes
// print them one after another
while($row = mysql_fetch_object($membersresult)) {
echo "<li>"."".$row->company.""."</li>";
}
}
else {
// no
// print status message
echo "No rows found!";
}
// free result set memory
mysql_free_result($membersresult);
// close connection
mysql_close($link);
?>
</ul>
</div>
<!-- Second Box: initially hidden with CSS "display: none;" -->
<div id="selectCompanyUser" class="panelNormal" style="display: none;">
<div class="splitter"></div>
</div>
You could just create #highlighter2 and make your code block into a function that takes the ID value and then just call it twice:
function hookupHighlight(id) {
var context = document.getElementById(id);
var items = context.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
// do AJAX stuff
// remove the "highlight" class from all list items
for (var j = 0; j < items.length; j++) {
var classname = items[j].className;
items[j].className = classname.replace(/\bhighlight\b/i, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
}, false);
}
}
hookupHighlight("highliter1");
hookupHighlight("highliter2");
jQuery would make this easier in a lot of ways as that entire block would collapse to this:
$("#highlighter1 li, #highlighter2 li").click(function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
If any of the objects you want to click on are not initially present when you run this jQuery code, then you would have to use this instead:
$("#highlighter1 li, #highlighter2 li").live("click", function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
change the replace in /highlight/ig, it works on http://jsfiddle.net/8RArn/
var context = document.getElementById('highlighter');
var items = context.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
// do AJAX stuff
// remove the "highlight" class from all list items
for (var j = 0; j < items.length; j++) {
var classname = items[j].className;
items[j].className = classname.replace(/highlight/ig, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
}, false);
}
So all those guys that are saying just use jQuery are handing out bad advice. It might be a quick fix for now, but its no replacement for actually learning Javascript.
There is a very powerful feature in Javascript called closures that will solve this problem for you in a jiffy:
var addTheListeners = function (its) {
var itemPtr;
var listener = function () {
// do AJAX stuff
// just need to visit one item now
if (itemPtr) {
var classname = itemPtr.className;
itemPtr.className = classname.replace(/\bhighlight\b/i, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
itemPtr = this;
}
for (var i = 0; i < its.length; i++) {
its[i].addEventListener ('click', listener, false);
}
}
and then:
var context = document.getElementById ('highlighter');
var items = context.getElementsByTagName ('li');
addTheListeners (items);
And you can call add the listeners for distinct sets of doc elements as many times as you want.
addTheListeners works by defining one var to store the list's currently selected item each time it is called and then all of the listener functions defined below it have shared access to this variable even after addTheListeners has returned (this is the closure part).
This code is also much more efficient than yours for two reasons:
You no longer iterate through all the items just to remove a class from one of them
You aren't defining functions inside of a for loop (you should never do this, not only for efficiency reasons but one day you are going to be tempted to use that i variable and its going to cause you some problems because of the closures thing I mentioned above)

Categories

Resources