Maintaining checkbox state in jQuery accordion - javascript

I have a nested accordion with a checkbox that makes an ajax call if checked, the problem is that the checkbox does not remain checked after the accordion expands.
if (childList.length > 0) {
list += '<ul class = ui-accordion>';
for (var j = 0; j < childList.length; j++) {
list += '<li><div><input type = "checkbox" class = "reqCheckBox" value="' + childList[i].ComponentID + '"/> ' + childList[j].ComponentDesc +'</div>';
var grandChildList = $.grep(data, function (n, k) {
return n.ParentID == childList[j].ComponentID;
}, false);
if (grandChildList.length > 0) {
list += '<ul class = ui-accordion>';
for (var k = 0; k < grandChildList.length; k++) {
list += '<li><div><input type = "checkbox" onclick="getReq(' + grandChildList[i].ComponentID + ')"/>' + grandChildList[k].ComponentDesc + '</div>';
var greatGrandChildList = $.grep(data, function (n, l) {
return n.ParentID == grandChildList[k].ComponentID;
}, false);
if (greatGrandChildList.length > 0) {
list += '<ul class = ui-accordion>';
for (var l = 0; l < greatGrandChildList.length; l++) {
list += '<li><div><input type = "checkbox" onclick="getReq(' + greatGrandChildList[i].ComponentID + ')"/>' + greatGrandChildList[l].ComponentDesc + '</div>';
}
list += '</li></ul>';
}
}
list += '</li></ul>';**
I know that I need to make this unobtrusive but any critiques of my JavaScript code as well would be appreciated as I am a noob. Thanks!

What you need is to persist the value of the checkbox. So when you check it your ajax calback needs to update the value of a property. This way when the accordion expands it persists the state of the checkbox.

I maintained checkbox state by adding this function
$("a").click(function (event) {
event.stopPropagation();
});
});

The following works.
$("#accordion h3 input").click(function(event) {
event.stopPropagation();
});

Related

How to create loop to add eventListener for each item in a list

Wracking my brains on this one but I'm sure it's just my inexperience with js.
I have a list of items and I am trying to create an eventListener for each item row so when I mouseover the row some icons will appear. The icons should hide again on mouseout.
I am using setAttribute to change the opacity of the elements on mouseover and mouseout events.
My problem is after the loop runs to create the eventListeners the setAttribute only affects the very last row of the loop - regardless of which row I mouse over.
I have a JSFiddle here which shows you the exact behaviour (better than I can explain): https://jsfiddle.net/Finno/ds9q7zju/27/
Note: this is a simplified example but it exhibits the same behaviour. My real app has the items formatted in a drop down menu.
Here is the code :
var total = 8;
var items;
var row;
var sort;
var trash;
for (var i = 0; i < total; i++) {
items = items + '<div id="row' + i + '">';
items = items + '<span class="hidden" id="sort' + i + '">SORT</span>';
items = items + '<span id="content' + i + '">Some content</span>';
items = items + '<span class="hidden" id="trash' + i + '">TRASH</span>';
items = items + '</div><br>';
}
document.getElementById("queue").innerHTML = items;
for (var i = 0; i < total; i++) {
row = 'row' + i;
sort = 'sort' + i;
trash = 'trash' + i;
document.getElementById(row).addEventListener("mouseover", function() {
document.getElementById(sort).setAttribute("style", "opacity:1 !important");
document.getElementById(trash).setAttribute("style", "opacity:1 !important");
});
document.getElementById(row).addEventListener("mouseout", function() {
document.getElementById(sort).setAttribute("style", "opacity:0 !important");
document.getElementById(trash).setAttribute("style", "opacity:0 !important");
});
}
You need to recreate the variables in each iteration, Try this,
Also, you can use string interpolation in place of + to join strings
var total = 8;
var items;
for (var i = 0; i < total; i++) {
items = items + '<div id="row' + i + '">';
items = items + '<span class="hidden" id="sort' + i + '">SORT</span>';
items = items + '<span id="content' + i + '">Some content</span>';
items = items + '<span class="hidden" id="trash' + i + '">TRASH</span>';
items = items + '</div><br>';
}
document.getElementById("queue").innerHTML = items;
for (var i = 0; i < total; i++) {
let row = 'row' + i;
let sort = 'sort' + i;
let trash = 'trash' + i;
document.getElementById(row).addEventListener("mouseover", function() {
document.getElementById(sort).setAttribute("style", "opacity:1 !important");
document.getElementById(trash).setAttribute("style", "opacity:1 !important");
});
document.getElementById(row).addEventListener("mouseout", function() {
document.getElementById(sort).setAttribute("style", "opacity:0 !important");
document.getElementById(trash).setAttribute("style", "opacity:0 !important");
});
}

Creating a list of students and assigning a function to each button

I am trying to allow clients to create a list of students then view more info by simply clicking on the button with the students name. I've got it to create the button and display the students name in the button but it only calls the function when I click submit to add the student to the list, the actual student button doesn't seem to function.
function updateStudentList() {
var html = "";
for (var i = 0; i < students.length; i++) {
html += "<li><button type='button' class='studentButton'" + "id=" + students[i].name +">" + students[i].name + "</button></li>";
}
$('#studentList').html(html);
for (var i = 0; i < students.length; i++) {
document.getElementById(students[i].name).addEventListener('click', openStudentInfo(students[i].name));
}
}
function openStudentInfo(studentName) {
console.log("Opening " + studentName + " info.");
var studentInfo = requestStudentByName(studentName);
if (studentInfo != null) {
var studentInfoForm = $("#studentInfoForm");
var html = "";
html += "<h3>Student Name: " + studentInfo.name + "</h3>";
html += "<h3>Student ID: " + studentInfo.studentID + "</h3>";
studentInfoForm.html(html);
$("#studentInfoModal").show();
}
}
HTML:
<ul data-role="listview" id="studentList"> </ul>
Note: I can't use the onclick tag in HTML, it causes security issues. Cordova also blocks this.
The way you binding the event is not ok. Try binding this way:
$(document).ready(function() {
$("#studentList").on("click", ".studentButton", function() {
var studentId = $(this).data("studentid");
openStudentInfo(studentId);
});
});
And in your HTML generation:
html += "<li><button type='button' class='studentButton' data-studentid='" + students[i].studentID +"'>" + students[i].name + "</button></li>";
This kind of event delagation works not metter how you create the elements inside the root element(studentList in this case), because the event was bound in it, and not on the dynamic elements.
no jquery version of DontVoteMeDown's answer
document.getElementById('studentList').addEventListener('click', function(event) {
var clickedEl = event.target;
if(clickedEl.className === 'studentButton') {
var studentId = clickedEl.dataset.studentId;
openStudentInfo(studentId);
}
});

Getting undefined when trying to fetch an ID from a data attribute

I can't figure out why am I getting undefined when trying to console.outthe iUsedId variable from the code below.
Here I attatch the user id to data-iUserId.
var aUsers = [];
for( var i = 0; i < aUsers.length; i++ ){
$("#lblUsers").append('<tr><th scope="row">'+aUsers[i].id+'</th><td>'+aUsers[i].username+'</td><td>'+aUsers[i].firstName+'</td><td>'+aUsers[i].lastName+'</td><td>'+aUsers[i].email+'</td><td>'+"<span data-iUserId='"+aUsers[i].id+"'</span><input type='checkbox' id='chk_"+i+"'"+'</td></tr>');
}
And here I am trying to use the data from the data attribute, but in the console all I get is undefined.
$(document).ready(function() {
$("#remove").on("click", function() {
$('input:checked').each(function() {
$(this).closest('tr').remove();
var iUserId = $(this).attr('data-iUserId');
console.log(iUserId);
for (var i = 0; i < aUsers.length; i++) {
if (iUserId == aUsers[i].iUsersId) {
aUsers.splice(i, 1);
}
}
});
});
});
Any gueses? Please help!
You are deleting the parent with the containers, then trying to access the element.
removing the parent should be in the last step:
$(document).ready(function() {
$("#remove").on("click", function() {
$('input:checked').each(function() {
var iUserId = $(this).closest('span').attr('data-iUserId');
console.log(iUserId);
for (var i = 0; i < aUsers.length; i++) {
if (iUserId == aUsers[i].iUsersId) {
aUsers.splice(i, 1);
}
}
$(this).closest('tr').remove();
});
});
});
Also, consider the comment of #pBuch
The reason is you are looping over the checkboxes and not the span's which have the attribute you are trying to access.
$(this) refers to the checkbox and not the span in the each method you are using:
$('input:checked').each(function() {
// Inside this each statement $(this) refers
// to the the current 'input:checked' element being accessed
});
You should put the data-iUserId attribute on the checkbox since you are accessing that element.
Also! You are missing the closing '>' on the opening span tag:
<span data-iUserId='"+aUsers[i].id+"'</span>
var aUsers = [];
//...somehow populate array...
// We have to assume here that the array got populated
for (var i = 0; i < aUsers.length; i++) {
$("#lblUsers").append('<tr><th scope="row">' + aUsers[i].id + '</th><td>' + aUsers[i].username + '</td><td>' + aUsers[i].firstName + '</td><td>' + aUsers[i].lastName + '</td><td>' + aUsers[i].email + '</td><td>' + "<span data-iUserId='" + aUsers[i].id + "'></span><input type='checkbox' id='chk_" + i + "'" + '</td></tr>');
}
$(document).ready(function() {
$("#remove").on("click", function() {
$("#lblUsers").find('input[type="checkbox"]:checked').each(function() {
// fixed to get the element with the data
var iUserId = $(this).siblings('[data-iUserId]').data('iuserid');
console.log(iUserId);
for (var i = 0; i < aUsers.length; i++) {
// bad practice to use a global aUsers
if (iUserId == aUsers[i].iUsersId) {
aUsers.splice(i, 1);
}
}
$(this).closest('tr').remove();
});
});
});

javascript get element in table untill a class happen

I have a huge table which most of the entries are "display:none". When the user click on an entity the rows should appear until the same class happen.
The table looks something like this:
<tbody>
<tr id="1" class="department"></tr>
<tr style="display:none;" id="43" class="sub"></tr>
<tr style="display:none;" id="55" class="sub"></tr>
<tr style="display:none;" id="85" class="sub"></tr>
<tr id="6" class="department"></tr>
<tr style="display:none;" id="150" class="sub"></tr>
</tbody>
So by clicking on id = 1 row the table should expand to show id= 43,55,85 (until reach to class="department" again)
I know it's a bit confusing so feel free to ask me questions if you need more explanation.
In plain javascript, you can do something like this:
function hasClass(elem, cls) {
var str = " " + elem.className + " ";
var testCls = " " + cls + " ";
return(str.indexOf(testCls) != -1) ;
}
var table = document.getElementById("myTable");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
(function(index) {
rows[index].addEventListener("click", function(e) {
for (var i = index + 1; i < rows.length; i++) {
var row = rows[i];
if (hasClass(row, "department")) {
break;
}
row.style.display = "";
}
});
})(i);
}
Working demo: http://jsfiddle.net/jfriend00/Dh3p3/
The code uses a closure to capture the row index for each row, such that when it is clicked on, you can use that index into the previously captured array of rows. It then walks down that array showing rows until it finds an item that has the "department" class.
FYI, this puts event listeners on all the rows so if you manually show one of the hidden rows, it can be clicked on and have the same behavior. If you only want click handlers on the class="department" rows, the code can easily be modified to do that too.
Here's a version that works with a hierarchy of classes. It expands only items at the next level on a click:
function hasClass(elem, cls) {
var str = " " + elem.className + " ";
var testCls = " " + cls + " ";
return(str.indexOf(testCls) != -1) ;
}
var table = document.getElementById("myTable");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
(function(index) {
rows[index].addEventListener("click", function(e) {
// nothing to do if clicking on the last item
if (index + 1 >= rows.length) {
return;
}
// get class name to stop on
var clsToStopOn = this.className;
// get class name to show
var clsToShow = rows[index + 1].className;
for (var i = index + 1; i < rows.length; i++) {
var row = rows[i];
if (hasClass(row, clsToStopOn)) {
break;
}
if (hasClass(row, clsToShow)) {
row.style.display = "";
}
}
});
})(i);
}
Working multi-level demo: http://jsfiddle.net/jfriend00/9HgPt/

'tag' field jQuery

I am creating a popup menu with jQuery with some buttons. Number of buttons is flexibel. For each item in the variable 'items' there is one button. The problem: If the button is clicked I want to know to which item it belongs to.
In other programming languages there is a so called 'tag'-field where you can put an integer or an object for identification. What is the best way in jQuery?
var popup = $('<div />');
for (var i = 0; i < items.length; i++)
{
var btn = $('<div>' + items.Name + '</div>').appendTo(popup).button().click(
function(e) {
// alert($(e.target).tag);
popup.hide();
}
);
// btn.tag = i;
}
Try looking at .data(). This will allow you to store information associated with a key for any element.
var popup = $('<div />');
for (var i = 0; i < items.length; i++)
{
var btn = $('<div>' + items.Name + '</div>').appendTo(popup).button().click(
function(e) {
// alert($(e.target).tag);
popup.hide();
}
);
btn.data("index", i);
}
You can the retrieve the information using btn.data("index") where btn is a jQuery object containing the element.
Update - Accessing the element that has been clicked within a function
The this keyword will refer to the html element that has been clicked. You could alert the stored data using:
function(e){
alert($(this).data("index"));
}
You can use the "data-" attribute...
var popup = $('<div />');
for (var i = 0; i < items.length; i++)
{
var btn = $('<div ' + 'data-tag="' + i + '">' + items.Name + '</div>').appendTo(popup).button().click(
function(e) {
// alert($(e.target).tag);
alert($(this).data("tag"));
popup.hide();
}
);
// btn.tag = i;
}

Categories