Getting next element in a table javascript - javascript

https://jsfiddle.net/en6jh7pa/1/
I am having issues grabbing the next element, it is returning null for the next element.
I am passing "this? as onclick and I assumed that you could use this to grab the next element but it seems that it instead returns null
Thanks for your help
function assignnames(checkboxelement){
checkboxelement.setAttribute("name", "checkbox");
var value1box = checkboxelement.nextSibling;
value1box.setAttribute("name", "notnull");
var value2box = checkboxelement.nextElementSibling;
value2box.setAttribute("name", "notnull");
alert("done");
}
<table border="1">
<tr>
<th>
Checkbox
</th>
<th>
value1
</th>
<th>
value2
</th>
</tr>
<tr>
<td>
<input type="checkbox" onclick="assignnames(this)" id="checkbox1"/>
</td>
<td>
<input type="text" name="" id="fname1">
</td>
<td>
<input type="text" name="" id="lname1">
</td>
</tr>
</table>

If you want to get the text inputs in the same row, you can go up to the row, then use a selector to get the inputs, e.g.
function getParent(node, tag) {
var tag = tag.toLowerCase();
do {
if (node.tagName.toLowerCase() == tag) {
return node;
}
node = node.parentNode;
} while (node && node.tagName && node.parentNode)
return null;
}
function getInputs(evt) {
var row = getParent(this, 'tr');
var inputs;
if (row) {
inputs = row.querySelectorAll('input[type="text"]');
}
console.log(`Found ${inputs.length} text inputs, node is ${this.checked? '':'not '}checked.`);
}
window.onload = function(){
document.getElementById('checkbox1').addEventListener('click', getInputs, false);
};
<table border="1">
<tr><th>Checkbox
<th>value1
<th>value2
<tr><td><input type="checkbox" id="checkbox1">
<td><input type="text" name="" id="fname1">
<td><input type="text" name="" id="lname1">
</table>

For the inputs to be siblings, they would all have to be within the same <td>, sharing a singular parent. With them spread out across multiple table cells, they would be considered cousins instead (keeping with the family tree metaphor), which doesn't have a similar shortcut property.
You can still use nextElementSibling along the way between inputs, but you'll also have to move up and back down between generations.
function assignnames(checkboxelement){
checkboxelement.setAttribute("name", "checkbox");
var value1box = checkboxelement
.parentElement // up a generation the checkbox' parent <td>
.nextElementSibling // then to the next <td> in the row
.firstElementChild; // and back down a generation to the next input
// the last step could also be: .querySelector('input')
value1box.setAttribute("name", "notnull");
var value2box = value1box
.parentElement
.nextElementSibling
.firstElementChild;
value2box.setAttribute("name", "notnull");
alert("done");
}
<table border="1">
<tr>
<th>
Checkbox
</th>
<th>
value1
</th>
<th>
value2
</th>
</tr>
<tr>
<td>
<input type="checkbox" onclick="assignnames(this)" id="checkbox1"/>
</td>
<td>
<input type="text" name="" id="fname1">
</td>
<td>
<input type="text" name="" id="lname1">
</td>
</tr>
</table>

Related

JavaScript function is failing to clone table row

To give you some context of my situation, In my my code an item can have parts, with these parts the user can harvest, transfer, or dispose of them. For example, a item can have 5 parts. Right now when the user selects a radio box option it will handle all of these parts the same way. So I have created a checkBox option where a user can 'de-select' the "All" option. I then want this to create two identical rows so that the user can have options, for example , transfer 2, harvest 2, dispose of 1.
I have a checkbox that calls my jQuery function that looks like this
I have a checkbox field inside my row that calls my jQuery function when it is clicked, it looks like this
<tr class="tr_clone">
<td>
#part.PartIDLink
</td>
<td>
#Html.DisplayFor(x => x.Parts[i].PartName)
#Html.HiddenFor(x => x.Parts[i].PartName)
</td>
<td style="font-weight:bold">
#Html.DisplayFor(x => x.Parts[i].QtyInItem)
#Html.HiddenFor(x => x.Parts[i].QtyInItem)
</td>
<td>
<input type="checkbox" id="all#(part.ID)" onchange="doalert(this.id, #part.ID)" checked>
</td>
#foreach (var actionType in partActionTypes)
{
<td>
#Html.RadioButtonFor(x => x.Parts[i].SelectedActionType, actionType)
</td>
}
</tr>
And here is my JQuery function
<script>
function doalert(id, rowID) {
var $tr = $(this).closest('.tr_clone');
var $clone = $tr.clone();
$clone.find('td');
$tr.after($clone);
}
</script>
Here is the rendered HTML
<table class="table">
<tbody>
<tr>
<th>
IGT Part ID
</th>
<th>
Part Name
</th>
<th>
Qty used in Item
</th>
<th>
Move All
</th>
<th style="color:blue">
Transfer
</th>
<th style="color:forestgreen">
Harvest
</th>
<th style="color:red">
Dispose
</th>
</tr>
</tbody>
<tr class="tr_clone" ">
<td>
<a p-id="346 " style="color:#FF00FF; " href="# ">600601</a>
</td>
<td>
Supply - Packing Carton, 9" x 8 " x 8", MU/AX <input id="Parts_0__PartName" name="Parts[0].PartName" type="hidden" value="Supply - Packing Carton, 9" x 8" x 8", MU/AX">
</td>
<td style="font-weight:bold">
1
<input data-val="true" data-val-number="The field QtyInItem must be a number." data-val-required="The QtyInItem field is required." id="Parts_0__QtyInItem" name="Parts[0].QtyInItem" type="hidden" value="1">
</td>
<td>
<input type="checkbox" id="all346" onchange="doalert(this.id,346)" checked="">
</td>
<td>
<input checked="checked" data-val="true" data-val-required="The SelectedActionType field is required." id="Parts_0__SelectedActionType" name="Parts[0].SelectedActionType" type="radio" value="Transfer">
</td>
<td>
<input id="Parts_0__SelectedActionType" name="Parts[0].SelectedActionType" type="radio" value="Harvest">
</td>
<td>
<input id="Parts_0__SelectedActionType" name="Parts[0].SelectedActionType" type="radio" value="Dispose">
</td>
</tr>
But it is not cloning the table row. Why is this? Any help is appreciated
The value of this is the issue, but Guy Incognito's hint doesn't tell the whole story. With jQuery, $(this) usually refers to the event target on a proper event handler. Since you aren't using one, it's null.
Instead of your inline change handler, bind the event in your script like this:
$('.tr_clone input.some-class').change(function() { // where some-class is on the checkbox
let Id = $(this).attr('id');
// set this attribute in your template
let partId = $(this).attr('data-partId');
// ...
}
To pass the value of partId from your template, use a data attribute:
<input type="checkbox" id="all#(part.ID)" data-partId="#(part.ID)" class="some-class">

Increment the id of html field [duplicate]

I have this table with some dependents information and there is a add and delete button for each row to add/delete additional dependents. When I click "add" button, a new row gets added to the table, but when I click the "delete" button, it deletes the header row first and then on subsequent clicking, it deletes the corresponding row.
Here is what I have:
Javascript code
function deleteRow(row){
var d = row.parentNode.parentNode.rowIndex;
document.getElementById('dsTable').deleteRow(d);
}
HTML code
<table id = 'dsTable' >
<tr>
<td> Relationship Type </td>
<td> Date of Birth </td>
<td> Gender </td>
</tr>
<tr>
<td> Spouse </td>
<td> 1980-22-03 </td>
<td> female </td>
<td> <input type="button" id ="addDep" value="Add" onclick = "add()" </td>
<td> <input type="button" id ="deleteDep" value="Delete" onclick = "deleteRow(this)" </td>
</tr>
<tr>
<td> Child </td>
<td> 2008-23-06 </td>
<td> female </td>
<td> <input type="button" id ="addDep" value="Add" onclick = "add()"</td>
<td> <input type="button" id ="deleteDep" value="Delete" onclick = "deleteRow(this)" </td>
</tr>
</table>
JavaScript with a few modifications:
function deleteRow(btn) {
var row = btn.parentNode.parentNode;
row.parentNode.removeChild(row);
}
And the HTML with a little difference:
<table id="dsTable">
<tbody>
<tr>
<td>Relationship Type</td>
<td>Date of Birth</td>
<td>Gender</td>
</tr>
<tr>
<td>Spouse</td>
<td>1980-22-03</td>
<td>female</td>
<td><input type="button" value="Add" onclick="add()"/></td>
<td><input type="button" value="Delete" onclick="deleteRow(this)"/></td>
</tr>
<tr>
<td>Child</td>
<td>2008-23-06</td>
<td>female</td>
<td><input type="button" value="Add" onclick="add()"/></td>
<td><input type="button" value="Delete" onclick="deleteRow(this)"/></td>
</tr>
</tbody>
</table>​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
jQuery has a nice function for removing elements from the DOM.
The closest() function is cool because it will "get the first element that matches the selector by testing the element itself and traversing up through its ancestors."
$(this).closest("tr").remove();
Each delete button could run that very succinct code with a function call.
Lots of good answers, but here is one more ;)
You can add handler for the click to the table
<table id = 'dsTable' onclick="tableclick(event)">
And then just find out what the target of the event was
function tableclick(e) {
if(!e)
e = window.event;
if(e.target.value == "Delete")
deleteRow( e.target.parentNode.parentNode.rowIndex );
}
Then you don't have to add event handlers for each row and your html looks neater. If you don't want any javascript in your html you can even add the handler when page loads:
document.getElementById('dsTable').addEventListener('click',tableclick,false);
​​
Here is working code: http://jsfiddle.net/hX4f4/2/
I would try formatting your table correctly first off like so:
I cannot help but thinking that formatting the table could at the very least not do any harm.
<table>
<thead>
<th>Header1</th>
......
</thead>
<tbody>
<tr><td>Content1</td>....</tr>
......
</tbody>
</table>
Here's the code JS Bin using jQuery. Tested on all the browsers. Here, we have to click the rows in order to delete it with beautiful effect. Hope it helps.
I suggest using jQuery. What you are doing right now is easy to achieve without jQuery, but as you will want new features and more functionality, jQuery will save you a lot of time. I would also like to mention that you shouldn't have multiple DOM elements with the same ID in one document. In such case use class attribute.
html:
<table id="dsTable">
<tr>
<td> Relationship Type </td>
<td> Date of Birth </td>
<td> Gender </td>
</tr>
<tr>
<td> Spouse </td>
<td> 1980-22-03 </td>
<td> female </td>
<td> <input type="button" class="addDep" value="Add"/></td>
<td> <input type="button" class="deleteDep" value="Delete"/></td>
</tr>
<tr>
<td> Child </td>
<td> 2008-23-06 </td>
<td> female </td>
<td> <input type="button" class="addDep" value="Add"/></td>
<td> <input type="button" class="deleteDep" value="Delete"/></td>
</tr>
</table>
javascript:
$('body').on('click', 'input.deleteDep', function() {
$(this).parents('tr').remove();
});
Remember that you need to reference jQuery:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
Here a working jsfiddle example:
http://jsfiddle.net/p9dey/1/
Use the following code to delete the particular row of table
<td>
<asp:ImageButton ID="imgDeleteAction" runat="server" ImageUrl="~/Images/trash.png" OnClientClick="DeleteRow(this);return false;"/>
</td>
function DeleteRow(element) {
document.getElementById("tableID").deleteRow(element.parentNode.parentNode.rowIndex);
}
try this for insert
var table = document.getElementById("myTable");
var row = table.insertRow(0);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
cell1.innerHTML = "NEW CELL1";
cell2.innerHTML = "NEW CELL2";
and this for delete
document.getElementById("myTable").deleteRow(0);
Yeah It is working great
but i have to delete from localstorage too, when user click button , here is my code
function RemoveRow(id) {
// event.target will be the input element.
// console.log(id)
let td1 = event.target.parentNode;
let tr1 = td1.parentNode;
tr1.parentNode.removeChild(tr1);// the row to be removed
// const books = JSON.parse(localStorage.getItem("books"));
// const newBooks= books.filter(book=> book.id !== books.id);
// console.log(books, newBooks)
// localStorage.setItem("books", JSON.stringify(newBooks));
}
// function RemoveRow(btn) {
// var row = btn.parentNode.parentNode;
// row.parentNode.removeChild(row);
// }
button tag
class Display {
add(book) {
console.log('Adding to UI');
let tableBody = document.getElementById('tableBody')
let uiString = `<tr class="tableBody" id="tableBody" data-id="${book.id}">
<td id="search">${book.name}</td>
<td>${book.author}</td>
<td>${book.type}</td>
<td><input type="button" value="Delete Row" class="btn btn-outline-danger" onclick="RemoveRow(this)"></td>
</tr>`;
tableBody.innerHTML += uiString;
// save the data to the browser's local storage -----
const books = JSON.parse(localStorage.getItem("books"));
// console.log(books);
if (!books.some((oldBook) => oldBook.id === book.id)) books.push(book);
localStorage.setItem("books", JSON.stringify(books));
}
Hi I would do something like this:
var id = 4; // inital number of rows plus one
function addRow(){
// add a new tr with id
// increment id;
}
function deleteRow(id){
$("#" + id).remove();
}
and i would have a table like this:
<table id = 'dsTable' >
<tr id=1>
<td> Relationship Type </td>
<td> Date of Birth </td>
<td> Gender </td>
</tr>
<tr id=2>
<td> Spouse </td>
<td> 1980-22-03 </td>
<td> female </td>
<td> <input type="button" id ="addDep" value="Add" onclick = "add()" </td>
<td> <input type="button" id ="deleteDep" value="Delete" onclick = "deleteRow(2)" </td>
</tr>
<tr id=3>
<td> Child </td>
<td> 2008-23-06 </td>
<td> female </td>
<td> <input type="button" id ="addDep" value="Add" onclick = "add()"</td>
<td> <input type="button" id ="deleteDep" value="Delete" onclick = "deleteRow(3)" </td>
</tr>
</table>
Also if you want you can make a loop to build up the table. So it will be easy to build the table. The same you can do with edit:)

How to get the label value from table row when row number is known, using Javascript

I have a table like this-
<table>
<tr>
<td>
<label id="lbl1" value="1">Label1</label>
<td>
<td>
Some data
</td>
<td>
Some data
</td>
</tr>
<tr>
<td>
<label id="lbl2" value="1">Label1</label>
<td>
<td>
Some data
</td>
<td>
Some data
</td>
</tr>
<tr>
<td>
<label id="lbl3" value="1">Label1</label>
<td>
<td>
Some data
</td>
<td>
Some data
</td>
</tr>
</table>
My problem is that I want to alert the value of label present in the second row's first column. Assume that I don't know label id means I know its pattern like lbl1,lbl2 or lbl3.. but not exactly what it is in the second row.
If you are okay to use jQuery use this fiddle
var label = $('table tr:eq(1) td:eq(0)').find("label").attr("value")
alert(label);
You can use something like next
var labels = document.getElementsByTagName("label");
for (var i=0; i<labels.length; i++)
if (labels[i].id && labels[i].id.indexOf("lbl") == 0){
//you have found the label in the first row
}
You Can get label value by class name
$("label[class=lblclass]").each(function() {var result= $(this).val(); });
(OR)
You can get the Particular Label Value by ID
function getlabel_value(){var result=$('#lbl1').val();}

if input field has a value found in array, do this (jQuery/Javascript)

I've got a page with a handful of input fields.
I need to find the fields with an array of values, and if so, .remove() the closest('tr')
The markup is similar to this
<table>
<tr>
<td>
<input type="text" value="this">
</td>
</tr>
<tr>
<td>
<input type="text" value="that">
</td>
</tr>
<tr>
<td>
<input type="text" value="them">
</td>
</tr>
</table>
I need to find "this" and "that", and if they are there, remove their <tr> container (and themselves) so I'd end up with:
<table>
<tr>
<td>
<input type="text" value="them">
</td>
</tr>
</table>
I've tried this:
jQuery(document).ready(function($){
var badfields = ['this', 'that'];
var fieldvalue = $('input[type="text"]').val();
if($.inArray(fieldvalue, badfields) > -1){
$(this).closest('tr').remove();
}
});
but it doesn't seem to want to work?
You need to iterate over all the fields using .each, so something like this:
$('input[type="text"]').each(function() {
var fieldvalue = $(this).val();
if ($.inArray(fieldvalue, badfields) > -1) {
$(this).closest('tr').remove();
}
});
Example: jsfiddle
You can be very concise sometimes with jQuery. jQuery has content selectors you can use for this type of purpose:
$("input[type=text][value=this], [value=that]").parents("tr").remove();
since you don't necessarily know this or that beforehand, you can do something like this:
var badfields = ['this', 'that'];
$(badfields).each(function(i) {
$("input[type=text][value=" + this + "]").parents("tr").remove();
});
You can use each to iterate through the selector. this in your inArray scope is not the element you were looking for.
DEMO: http://jsfiddle.net/
html:
<table>
<tr>
<td>
<input type="text" value="this">
</td>
</tr>
<tr>
<td>
<input type="text" value="that">
</td>
</tr>
<tr>
<td>
<input type="text" value="them">
</td>
</tr>
</table>
js:
jQuery(document).ready(function($){
var badfields = ['this', 'that'];
$('input[type="text"]').each(function(){
if( $.inArray(this.value, badfields) > -1 ){
$(this).closest('tr').remove();
}
});
});

How to use javascript to select a row of radio buttons when a checkbox is selected

What I need to do is check to see if the checkbox is checked, and if so, select all the radio buttons located within the same element?
I've set up the elements with id's, b/c that is the "physical" grouping of the elements that will be affecting each other.
I'm trying to do something like this onchange('some_row_id'):
function select_row(row_id) {
// Get 1st td element (where checkbox is located) and assign its
// checked value to variable "checked"
var checked = document.getElementById(row_id).td[0].input.checked;
if(checked) {
var children = document.getElementById(row_id).childNodes;
for(var i = 0; i< children.length; i++) {
if(children.td.type == radio) {
children.td.radio = checked;
}
}
}
}
I know that javascript is almost 200% wrong, but I can't figure out how to properly select only td children (or prefereably, only input grandchildren) of a tr element and check them.
Here's the basic form structure in actual working html:
<form name="form2" action="testfile4.php" method="get">
<table border="1"><thead>
<tr><th>Select entire row</th><th>item_code</th><th>description</th><th>page</th>
</tr>
</thead>
<tbody>
<tr id="534">
<td ><input type="checkbox" onchange="select_row(534);"></td> <td>15038 <input type="radio" name="15819-038|item_code" value="534" /></td>
<td>For 1st item, alternate 1
<input type="radio" name="15819-038|description" value="534" /></td>
<td>5
<input type="radio" name="15819-038|page" value="534" /></td>
</tr>
<tr id="535">
<td ><input type="checkbox" onchange="select_row(535);"></td> <td>15038 <input type="radio" name="15819-038|item_code" value="535" /></td>
<td>For 1st item, alternate 2 <input type="radio" name="15819-038|description" value="535" /></td>
<td>5
<input type="radio" name="15819-038|page" value="535" /></td>
</tr>
</tbody>
</table>
</form>
EDIT:
I'm willing to accept jquery solutions. Thank you.
EDIT 2:
Thanks to nnnnnn. I used your JS and added this to have the uncheck behavior I wanted. If you want you can update your answer with it and I'll remove it from here:
else if (!row.cells[0].childNodes[0].checked) {
inputs = row.getElementsByTagName("input");
for(var i=0; i<inputs.length; i++) {
if(inputs[i].type === "radio") {
inputs[i].checked = false;
}
}
}
Well here's one way to do it:
function select_row(row_id) {
// get a reference to the row based on the id passed in
var row = document.getElementById(row_id),
inputs;
// .cells[0] gives the row's first td element,
// then .childNodes[0] gives that td's first child element which
// will be the checkbox
if (row.cells[0].childNodes[0].checked) {
// getElementsByTagName gives all descendent elements with the matching
// tag, not just direct children, so get all the inputs for the current
// row and loop through them looking for the radios
inputs = row.getElementsByTagName("input");
for (var i=0; i<inputs.length; i++) {
if (inputs[i].type==="radio")
inputs[i].checked = true;
}
}
}
And change your checkbox to use onclick=... instead of onchange=....
Note that using checkboxes to select a row doesn't really make sense because after selecting one, if you click another row's checkbox the first row's checkbox is still checked. You might be better off with a button or <a> element for this purpose.
Note also that instead of passing the row ID to the function and then getting a reference to that row using the ID, like this:
<input type="checkbox" onclick="select_row(534)">
function select_row(row_id) {
var row = document.getElementById(row_id);
// etc
You can directly pass a reference to the checkbox that was clicked and use that instead:
<input type="checkbox" onclick="select_row(this);">
function select_row(cb) {
var row = cb.parentNode.parentNode;
if (cb.checked) {
// etc
However in my full solution above I stuck with passing the ID like you did in case you are calling the function from somewhere other than just the click event.
There were several things wrong with your code as posted:
You can't get children of a particular element just by refering to their type, e.g., getElementById(row_id).td[0].input doesn't work. To get the first td you can use a row's cells collection and say .cells[0] (like in my code).
Your for loop doesn't use the i variable within the loop to select the individual items. You should've said children[i] within the loop.
Your if statement: if(children.td.type = radio) { is doing an assignment (single = sign) instead of a comparison (double == or triple === equals sign), and should be comparing to the string "radio" (with quotes).
If you want jquery solution then here it is:
$(document).ready(function() {
$("#your_checkbpx_id").click(function() {
if($(this).is(":checked")) {
//select all your radio
var id = $(this).attr("id");
$("tr[id='"+id+"'] > input[type='radio']").each(function() {
//make it checked
$(this).attr("checked", "checked");
});
}
});
});
Hope it helps
Here is a way:
<script type="text/javascript">
/*
Loops through all input elements finding all checkboxs.
Testing if the current checkbox obj is equal to the selected checkbox object.
If equal: Set the radio button to the current state of the checkbox.
Not equal: Set its children radio buttons checked property false
--------------------------------------------------------------------------- */
function setCheckboxs(checkBox) {
var parent, i;
var checkBoxs = document.getElementsByTagName("input");
for (i = 0; i < checkBoxs.length; i++) {
if (checkBoxs[i].getAttribute("type") === "checkbox") {
parent = checkBoxs[i].parentNode.parentNode;
if (checkBox === checkBoxs[i]) {
toggleRadios(parent.childNodes, checkBoxs[i].checked);
} else {
checkBoxs[i].checked = false;
toggleRadios(parent.childNodes, false);
}
}
}
}
/*
Loops through all its children nodes finding a node's attribute equal to radio
----------------------------------------------------------------------------------- */
function toggleRadios(radios, isChecked) {
var i;
for (i = 0; i < radios.length; i++) {
if (radios[i].childNodes.length > 0) {
toggleRadios(radios[i].childNodes, isChecked);
}
if (radios[i].nodeName === "INPUT") {
if (radios[i].getAttribute("type") === "radio") {
radios[i].checked = isChecked;
}
}
}
}
</script>
<table border="1">
<thead>
<tr>
<th>
Select entire row
</th>
<th>
item_code
</th>
<th>
page
</th>
<th>
usd_dn
</th>
</tr>
</thead>
<tbody>
<tr id="534" class="15838">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
15838<input type="radio" name="15838|item_code" value="534" />
</td>
<td>
284<input type="radio" name="15838|page" value="534" />
</td>
<td>
$73.00<input type="radio" name="15838|usd_dn" value="534" />
</td>
</tr>
<tr id="535" class="15838">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
15838
<input type="radio" name="15838|item_code" value="535" />
</td>
<td>
299
<input type="radio" name="15838|page" value="535" />
</td>
<td>
$73.00<input type="radio" name="15838|usd_dn" value="535" />
</td>
</tr>
<tr id="565">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
1611
<input type="radio" name="1611|item_code" value="565" />
</td>
<td>
66<input type="radio" name="1611|page" value="565" />
</td>
<td>
$3,350.00
<input type="radio" name="1611|usd_dn" value="565" />
</td>
</tr>
<tr id="566">
<td>
<input type="checkbox" onclick="setCheckboxs(this);" />
</td>
<td>
1611
<input type="radio" name="1611|item_code" value="566" />
</td>
<td>
66<input type="radio" name="1611|page" value="566" />
</td>
<td>
$3,225.00
<input type="radio" name="1611|usd_dn" value="566" />
</td>
</tr>
</tbody>
</table>

Categories