Getting Cannot read property 'saveUpload' of undefined while passing props via function - javascript

var Admin = React.createClass({
saveUpload: function(id) {
alert(id);
},
getInitialState() {
return {
uploads: []
};
},
componentDidMount() {
var self = this;
$.ajax({
url: 'http://localhost:8080/admin/uploads',
success: function(data) {
self.setState({
uploads: data
})
}
});
},
render: function() {
var obj = this.state.uploads.map(function(product) {
return (
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>
)
});
return (
< div >
<div className = "container" >
<br / >
<h1 className = "text-center" > Welcome Admin < /h1>
<br / > < br / >
<hr / >
</div>
<h3 className = "text-center" > Company Upload Details < /h3>
<div className = "container" >
<table className = "table" >
<thead className = "thead-light" >
<tr >
<th > Id < /th> <th > CompanyName < /th>
<th > Date & Time < /th> <
th > FileName(csv) < /th> <
th > Size(KB) < /th> <
th > Status < /th> <
/tr> <
/thead> {
obj
} <
/table>
</div> </div>
)
}
});
here is the uploads component
var Uploads = React.createClass({
show() {
this.props.saveHandle(this.props.product.id);
},
render() {
return (
<tr>
<td> {this.props.product.id} </td>
<td> {this.props.product.company.companyName} </td>
<td> {(new Date(this.props.product.date)).toString()} </td>
<td> {this.props.product.fileName} </td>
<td> {this.props.product.filesize} </td>
<td> {this.props.product.status} </td>
<td>
<button className = "button" onClick = {this.show}> Save </button>
</td>
</tr>
)
}
});
Here is my code i am passing id from Uploads component to admin component when save button is clicked but it gives me an error that saveUpload is not defined.
I am confused my it is giving me that error i have a function saveUpload in Admin Component. what is wrong in this code

The bug is here:
var obj = this.state.uploads.map(function(product) {
return (
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>
)
});
Inside the map(), this is no longer the instance of your Admin component, it is window. If you bind it like so:
var obj = this.state.uploads.map(function(product) {
return (
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>
)
}.bind(this));
Then this will point to the Admin instance and you should get the function you're expecting. If you have ES6 available, you could also write it like this:
var obj = this.state.uploads.map(product =>
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>);
Using the "fat arrow" => lambda expression, this is automatically bound to the enclosing scope inside, saving you some effort.

Related

Problem with querying trough two tables in javascript/react

I was sitting with my problem for few hours already trying different solutions but nothing seems to work properly. I am displaying cards on my webpage fetched from database. Under each card I want to display Book a ticket button based on if ticket is available (if particular concert have a ticket and if the ticket is not used).
I have two tables: concerts and tickets There is over 2000 concerts but only 68 tickets for around 20 - 30 concerts. So most of concerts don't have a ticket at all, and some of concerts have multiple tickets, and some concerts have one ticket.
What I tried to do was to loop trough concerts then nest loop for tickets inside and get concerts which has a ticket but then I realized I also need to check if ticket is used to be able to properly display a button. It just getting too messy and way too complex in order to display a regular button.
Is there some way around it? Do I need to change my data base structure? What I actually need to do?
So once again: I need to display a button Book a ticket if particular concert has a ticket/tickets and if that ticket/tickets are unused (at least one unused), otherwise button should be gray with another text on it. Any suggestions?
Ticket table:
Concert table
And here is how my page look like:
UPDATE:
I managed to make a function to get all concerts with non booked tickets:
let concertsWithTickets = [];
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
concertsWithTickets.push(filteredData[j]);
}
}
}
Then i try to loop again inside the view but i got syntax error.
<div>
{for(let i = 0; concerts.length < i; i++)
{
if(concertsWithTickets.id == concert.id) ? <BookBtn/> :
<BookBtn color="gray"/>}
}
</div>
Here is the whole code without import stuff
useEffect(() => {
let concertsWithTickets = [];
const loadConcerts = async () => {
const resConcerts = await axios.get("/data/concerts");
const resTickets = await axios.get("/data/tickets");
// getting all concerts above today
const filteredData = resConcerts.data.filter((concert) => {
return concert.datum >= currentDate;
});
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
concertsWithTickets.push(filteredData[j]);
}
}
}
setConcerts(
filteredData.sort((a, b) =>
a.datum > b.datum ? 1 : a.datum < b.datum ? -1 : 0
)
);
};
loadConcerts();
console.log(concertsWithTickets);
}, []);
if (!concerts.length) {
return <p className="center">Loading...</p>;
}
return (
<div className="center">
<h1 className="textpink">Upcoming concerts </h1>
<hr className="stylez" />
{concerts.slice(0, limit ? limit : concerts.length).map((concert)
=> {
return (
<div className="cards-container " key={concert.id}>
<div className="card">
<h3>
{concert.name.length > 32
? concert.name.slice(0, 32) + "..."
: concert.name}
</h3>
<h2>{concert.id}</h2>
<img
src={concert?.image ? concert.image : defaultpicture}
alt="Band-Image"
/>
<div>
<p className="label">{concert.datum}</p>
<p className="cardAdress">
{concert.venue.length > 30
? concert.venue.slice(0, 27) + "..."
: concert.venue}
</p>
</div>
<div>
{for(let i = 0; concerts.length < i; i++)
{
if(concertsWithTickets.id == concert.id) ? <BookBtn /> : <BookBtn color="gray"/>}
}
</div>
</div>
</div>
);
})}
<div>
<hr className="stylez" />
<button className="btn-singup " onClick={() => setLimit(limit + 5)}>
Show more
</button>
</div>
</div>
);
};

React: this.setState not resetting state

I have some data I'm distilling to render, essentially I'd like to have a checkbox to reset the state and DOM as it would when the page loads.
Initially I had a selected property on the state and a conditional to make sure it was working. That worked. But I can't get it to work, what am I missing?
UPDATE May 9th 2018
As Jay suggested below I am going to put the whole module in a snippet and focus on the parts which are the crux of the question/problem,
The whole module is in a snippet below...
I have a component that displays an array of objects, and each object is getting distilled into its own card. Below is a screenshot for clarity.
Here is my method in my component:
handleReset() {
this.setState({
data: this.props.data,
});
}
And this is the JSX which is being rendered.
<label>
<input type="checkbox" onChange={this.handleReset} />
<b>reset</b>
</label>
With some time to think about this I realize that my handeReset is not doing anything is probably because it is just rendering the state as it is now. So how my question is how do you go back to the way the UI looked initially? Pre sorting?
import React, {
Component
} from 'react';
import {
Card,
Select,
Segment,
Container,
Divider,
Grid,
Header,
Image
} from 'semantic-ui-react';
import '../css/app.css';
class FilterOptions extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
priority: '',
category: '',
selected: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleReset = this.handleReset.bind(this);
}
handleReset() {
this.setState({
data: this.state.data,
});
}
handleChange(e) {
var val = e.target.value;
if (!isNaN(val)) {
this.setState({
priority: val
});
} else if (isNaN(val)) {
this.setState({
category: val
});
}
this.props.changeOption(val);
}
render() {
var reset;
if (!this.state.data) {
reset = 'reset';
} else {
reset = 'not reset';
}
return ( <
div >
<
h5 > By category < /h5> <
label >
<
input type = "checkbox"
onChange = {
this.handleReset
}
/>
reset {
reset
} <
/label> <
h5 > By category < /h5> <
ul >
<
li >
<
label >
<
input type = "radio"
value = "cat1"
checked = {
this.state.category === 'cat1'
}
onChange = {
this.handleChange
}
/>
cat1 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "cat2"
checked = {
this.state.category === 'cat2'
}
onChange = {
this.handleChange
}
/>
cat2 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "cat3"
checked = {
this.state.category === 'cat3'
}
onChange = {
this.handleChange
}
/>
cat3 <
/label> <
/li> <
/ul> <
h5 > By priority < /h5> <
ul >
<
li >
<
label >
<
input type = "radio"
value = "1"
checked = {
this.state.priority === '1'
}
onChange = {
this.handleChange
}
/>
1 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "2"
checked = {
this.state.priority === '2'
}
onChange = {
this.handleChange
}
/>
2 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "3"
checked = {
this.state.priority === '3'
}
onChange = {
this.handleChange
}
/>
3 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "4"
checked = {
this.state.priority === '4'
}
onChange = {
this.handleChange
}
/>
4 <
/label> <
/li> <
/ul> {
/*<h5>By Color</h5>
<ul>
<li>
<label>
<input type="radio" value="Orange" checked={this.state.color === 'Orange'} onChange={this.handleChange} />
<div className="circle orange-filter-bg" />
</label>
</li>
<li>
<label>
<input type="radio" value="Green" checked={this.state.color === 'Green'} onChange={this.handleChange} />
<div className="circle green-filter-bg" />
</label>
</li>
<li>
<label>
<input type="radio" value="Blue" checked={this.state.color === 'Blue'} onChange={this.handleChange} />
<div className="circle blue-filter-bg" />
</label>
</li>
<li>
<label>
<input type="radio" value="Purple" checked={this.state.color === 'Purple'} onChange={this.handleChange} />
<div className="circle purple-filter-bg" />
</label>
</li>
</ul>*/
} <
/div>
);
}
}
function FilterUsers(props) {
return ( <
Container >
<
br / >
<
br / >
<
Grid columns = {
3
}
doubling stackable > {
props.data.map((user /* leveraging arrow functions implicit return */ ) => ( <
Grid.Column key = {
user.name
} >
<
Segment className = {
`priority${user.priority}`
} >
<
Card >
<
Card.Content >
<
Card.Header >
<
h2 > name: {
user.name
} < /h2> <
/Card.Header> <
Card.Meta >
<
span className = "card__age" > age: {
user.age
} < /span> <
/Card.Meta> <
Card.Description > priority: {
user.priority
} < /Card.Description> <
Card.Description className = "card__catergory" > category: {
user.category
} < /Card.Description> <
/Card.Content> <
/Card> <
/Segment> <
/Grid.Column>
))
} <
/Grid> <
/Container>
);
}
export default class SortAndFilterForm extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
priority: '',
category: '',
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(val) {
if (!isNaN(val)) {
this.setState({
priority: val
});
var filteredByPriority = this.props.data.filter(function(item) {
return parseInt(item.priority) === parseInt(val);
});
} else {
this.setState({
category: val
});
var filteredByPriority = this.props.data.filter(function(item) {
return item.category === val;
});
this.setState({
category: val
});
}
console.log('filteredByPriority', filteredByPriority);
this.setState({
data: filteredByPriority
});
}
render() {
return ( <
Container >
<
h1 > Sorts < /h1> <
FilterOptions data = {
this.state.data
}
changeOption = {
this.handleChange
}
/> <
FilterUsers data = {
this.state.data
}
/> <
/Container>
);
}
}
I am guessing the issue is here:
handleReset() {
this.setState({
data: this.state.data,
});
}
where you set the state.data to state.data, which unsurprisingly doesn't change anything. I imagine you want to do:
handleReset() {
this.setState({
data: this.props.data,
});
}
Your HandleReset() method should be setting everything in state as it was at the start:
this.state = {
data: this.props.data,
priority: '',
category: '',
};
And your cards are rendered with props.data.map meaning they would remain unaffected with changes in state. You should use data from state to render them out.
If I were you, I would turn FilterOptions into a purely functional component (e.g accepts only props, has no constructor, no state, move handleReset and handleChange upwards to SortAndFilterForm and pass them back down via props).
In SortAndFilterForm's constructor(), I would store a copy of its initial state data element (which has been passed to it as props.data from something else) as a state variable (n.b. Object.assign creates a shallow shallow copy which seems like it should work here but it depends on the contents and how you mutate them elsewhere):
this.state = {
data: props.data,
initData: Object.assign({}, props.data),
priority: '',
category: ''
}
The handleReset method in SortAndFilterForm's would then look like this:
handleReset() {
this.setState({
data: Object.assign({}, this.state.initData)
})
}
You will of course need to bind that in SortAndFilterForm's constructor():
this.handleReset = this.handleReset.bind(this)
...and pass it down to FilterOptions:
<FilterOptions
data={this.state.data}
changeOption={this.handleChange}
resetOption={this.handleReset} />
I believe all of the above should take care of your problem.

Checkbox default checked state issue

I am having the following code. There's some issue I'm facing right now with this thing in my project. I want to use the forEach() loop there inside getElements() instead of map() and also I want to simply show it default checked whenever after checking on a checkbox, going next and again returning back there.
Any help with this issue ??
here's the => DEMO
import React, { Component } from 'react';
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Checkbox from 'material-ui/Checkbox';
class App extends Component {
itemsPerPage = 4
constructor(props) {
super(props);
var ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
this.state = {
ids: ids,
idsChecked: ids.map(() => false),
page: 0
}
}
componentDidMount = () => {
}
handlePrevious = () => {
this.setState({ page: this.state.page - 1 });
}
handleNext = () => {
this.setState({ page: this.state.page + 1 });
}
handleCheck = (e) => {
var id = Number(e.currentTarget.id);
var idsChecked = this.state.idsChecked.map((bool, i) => i === id ? !bool : bool);
this.setState({ idsChecked: idsChecked });
}
handleDetails = (e) => {
var id = Number(e.currentTarget.getAttribute("rel"));
console.log("even or odd is clicked! (button #id: " + id + ")");
}
getElements = () => {
var first = this.state.page * this.itemsPerPage;
var trs = this.state.ids.slice(first, first + this.itemsPerPage).map((element, i) => {
let details = <button rel={first + i} onClick={this.handleDetails}> {element % 2 ? "odd" : "even"} </button>;
return (
<tr key={element}>
<td><Checkbox
checked={this.state.idsChecked[first + i]}
id={first + i}
onCheck={this.handleCheck}
/></td>
<td>{element}</td>
<td>{details}</td>
</tr>
);
});
return trs;
}
render() {
var hasPrevious = this.state.page > 0;
var hasNext = this.state.page < Math.floor((this.state.ids.length - 1) / this.itemsPerPage);
var tdStyle = {width: "80px"}
return (
<div>
<div>
<table>
<tbody>
<tr>
<td style={tdStyle}>{hasPrevious && <button onClick={this.handlePrevious} hidden={this.state.hasPrevious}> Previous </button>}</td>
<td style={tdStyle}>{hasNext && <button onClick={this.handleNext} hidden={this.state.isNext}> Next </button>}</td>
</tr>
</tbody>
</table>
</div>
<div>
<table>
<tbody>
{this.getElements()}
</tbody>
</table>
</div>
</div>
);
}
}
render(<MuiThemeProvider><App /></MuiThemeProvider>, document.getElementById('root'));
To replace, map with forEach, push the checkbox elements onto an array, and return that array from your getElements().
Use the defaultChecked props of the <Checkbox> component to set the default value to true.
Full code:
getElements = () => {
var first = this.state.page * this.itemsPerPage;
let checkboxArray = []; // array for storing the elements
this.state.ids.slice(first, first + this.itemsPerPage).forEach((element, i) => {
let details = <button rel={first + i} onClick={this.handleDetails}> {element % 2 ? "odd" : "even"} </button>;
checkboxArray.push(
<tr key={element}>
<td><Checkbox
checked={this.state.idsChecked[first + i]}
id={first + i}
defaultChecked={true/*use the defaultChecked prop*/}
onCheck={this.handleCheck}
/></td>
<td>{element}</td>
<td>{details}</td>
</tr>
);
});
return checkboxArray; // return the array
}
render() {
var hasPrevious = this.state.page > 0;
var hasNext = this.state.page < Math.floor((this.state.ids.length - 1) / this.itemsPerPage);
var tdStyle = {width: "80px"}
return (
<div>
<div>
<table>
<tbody>
<tr>
<td style={tdStyle}>{hasPrevious && <button onClick={this.handlePrevious} hidden={this.state.hasPrevious}> Previous </button>}</td>
<td style={tdStyle}>{hasNext && <button onClick={this.handleNext} hidden={this.state.isNext}> Next </button>}</td>
</tr>
</tbody>
</table>
</div>
<div>
<table>
<tbody>
{this.getElements()}
</tbody>
</table>
</div>
</div>
);
}
}

how to bind sum of string array values to $scope

I am trying to bind the sum of selected checkboxes from a table. I am almost there but I cannot figure out what I am doing wrong. The picture shows 2 selected boxes
you see the result of my code. I am open to suggestions if there is a better way of going about this.
$http.get('/api/Products/').success(function (data, status) { $scope.productList = data; });
$scope.selection = [];
$scope.OrderAmount = []
$scope.myTotal = 0;
$scope.toggleSelection = function toggleSelection(ProductId) {
var idx = $scope.selection.indexOf(ProductId);
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
else {
$scope.selection.push(ProductId);
}
for (var i = 0; i < $scope.selection.length; i++) {
var OrderProductId = $scope.selection[i]
var data = Enumerable.From($scope.productList).Where("x => x.ProductId == '" + OrderProductId + "'").ToArray();
$scope.OrderAmount.push(data[0].ProductPrice)
// $scope.OrderAmount = ["8500", "8500"]
for (var i = 0, len = $scope.OrderAmount.length; i < len; i++) {
$scope.myTotal += $scope.OrderAmount[i][0];
};
};
$scope.$watch('myTotal', function (value) {
$scope.model.OrderAmount = value;
});
};
view
<table class="table">
<th>Product</th>
<th>Price</th>
<tbody>
<tr ng-repeat="model in products">
<td>
<div class="toggle-switch" data-ts-color="blue">
<input id="{{model.ProductId}}" type="checkbox" hidden="hidden" ng-checked="selection.indexOf(model.ProductId) > -1" ng-click="toggleSelection(model.ProductId)">
<label for="{{model.ProductId}}" class="ts-helper"></label>
</div>
</td>
<td>{{model.ProductName}}</td>
<td>{{model.ProductPrice}}</td>
</tr>
</tbody>
</table>
<div class="form-group">
<input type="text" ng-model="model.OrderAmount" class="form-control fg-input">
</div>
UPDATE to first answer
You are doing the data binding wrongly. The checked status should be bound using ng-model but not ng-checked. You can make this easy by using an attribute (in the example checked) inside model and then loop over products to calculate the sum.
<tr ng-repeat="model in products">
<td>
<div class="toggle-switch" data-ts-color="blue">
<input id="{{model.ProductId}}" type="checkbox" hidden="hidden" ng-model="model.checked" ng-click="toggleSelection()">
<label for="{{model.ProductId}}" class="ts-helper"></label>
</div>
</td>
<td>{{model.ProductName}}</td>
<td>{{model.ProductPrice}}</td>
</tr>
Controller:
$scope.toggleSelection = function() {
var sum = 0;
angular.forEach($scope.products, function(value){
if (value.checked) sum += value.ProductPrice;
});
$scope.model.OrderAmount = sum;
}

jQuery - get selected value in dropdownlist when looping through a table

I've written this javascript to get the Id of a table then to loop through the tr's first then the td's. I am not sue what logic to write to get the selected value of the dropdown in a td. Not all tds, have a dropdown.
This is my javascript
function submitPanel(value) {
$('#' + value + '> tbody > tr').each(function () {
alert($(this).html());
$(this).find('td').each(function () {
alert($(this).html());
})
});
}
Which output this:
The table is made in MVC 4 razor
#model IMEModels.InterviewManagement.InterviewManagement
<hr />
#using (Html.BeginForm("SubmittedInterviews", "InterviewManagement", FormMethod.Post))
{
if (Model.InterviewSchedules.Count > 0)
{
<table>
<tr>
<td>#Html.Label("Show dates without Chair or Co-panelist") </td>
<td>#Html.RadioButton("Show dates without Chair or Co-panelist", new {Id = "rdoShow" })</td>
</tr>
</table>
for (int i = 0; i < Model.Centres.Count; i++)
{
#Html.Label(Model.Centres[i].CentreName)
for (int ii = 0; ii < Model.Centres[i].Locations.Count; ii++)
{
#Html.Label(Model.Centres[i].Locations[ii].LocationName)
for (int iii = 0; iii < Model.Centres[i].Locations[ii].InterviewDates.Count; iii++)
{
var ChairList = Model.Interviewers.Join(Model.DatePreferences, m => m.InterviewerId, d => d.InterviewersInterviewerId, (m, d) => new
{
Interviewer = m,
DatePreferences = d
})
.Where(d => d.DatePreferences.LocKey == Convert.ToString(Model.Centres[i].Locations[ii].LocationKey) && d.Interviewer.IsChair && d.DatePreferences.Date == Model.Centres[i].Locations[ii].InterviewDates[iii].Date)
.GroupBy(x => new { x.Interviewer.InterviewerId, x.Interviewer.Name })
.ToDictionary(a => a.Key.InterviewerId, b => b.Key.Name);
var NonChairList = Model.Interviewers.Join(Model.DatePreferences, m => m.InterviewerId, d => d.InterviewersInterviewerId, (m, d) => new
{
Interviewer = m,
DatePreferences = d
})
.Where(d => d.DatePreferences.LocKey == Convert.ToString(Model.Centres[i].Locations[ii].LocationKey) && d.DatePreferences.Date == Model.Centres[i].Locations[ii].InterviewDates[iii].Date)
.GroupBy(x => new { x.Interviewer.InterviewerId, x.Interviewer.Name })
.ToDictionary(a => a.Key.InterviewerId, b => b.Key.Name);
#:<div class="date-wrap #(ChairList.Count == 0 || NonChairList.Count == 0 ? "nochairspanelists" : "chairspanelists") >
if (NonChairList.Count == 0)
{
NonChairList.Add(new Guid(), "No panelists available.");
}
if (ChairList.Count == 0)
{
ChairList.Add(new Guid(), "No panelists available.");
}
#Html.Label(Model.Centres[i].Locations[ii].InterviewDates[iii].Date.ToLongDateString())
<table id="tbl#(Model.Centres[i].Code + "-" + Model.Centres[i].Locations[ii].LocationKey + "-" + Model.Centres[i].Locations[ii].InterviewDates[iii].Date.Ticks)" class="tblInterviewManager">
<tr>
<td>
Chair
</td>
<td>
Co-panelist
</td>
<td></td>
</tr>
<tr>
<td>
#Html.DropDownListFor(m => m.InterviewSchedules[iii].ChairId, new SelectList(ChairList, "Key", "Value"))
<br />
</td>
<td>
#Html.DropDownListFor(m => m.InterviewSchedules[iii].CofacilitatorId, new SelectList(NonChairList, "Key", "Value"))
</td>
#if (ChairList.ElementAt(0).Value == "No panelists available." || NonChairList.ElementAt(0).Value == "No panelists available.")
{
<td>
<input type="submit" value="Save panel" disabled="disabled" />
</td>
}
else
{
<td>
<input type="button" value="Save panel" id="btnSubmit" onclick="return submitPanel('tbl#(Model.Centres[i].Code + "-" + Model.Centres[i].Locations[ii].LocationKey + "-" + Model.Centres[i].Locations[ii].InterviewDates[iii].Date.Ticks)');"/>
</td>
}
</tr>
</table>
#:</div>
}
}
<br />
}
<div class="clear"></div>
<hr />
}
}
Anyone know a good way to get the values I want.
Use Descendant Selector (“ancestor descendant”) to directly get the selects within the table.
function submitPanel(value) {
$('#' + value + ' select').each(function () {
alert($(this).val());
});
}
Selects all elements that are descendants of a given ancestor, A
descendant of an element could be a child, grandchild,
great-grandchild, and so on, of that element, jQuery docs.

Categories