I've been trying to understand how to (in jQuery) traverse the DOM using $(this) as a starting point inside a setInterval/setTimeOut function and have come across some behavior I have found baffling.
It doesn't seem to be possible to traverse the DOM using $(this) as a starting point inside a setInterval/setTimeOut function
Why is that?
jQuery/javaScript people - get on that.
Sample code to illustrate the behaviour:
jQuery:
// set this code anywhere (inside/outside - document.ready/named-function/...) and run it
var autoTimer = setInterval(function(){
// INSERT EXAMPLE TEST-CODE FROM BELOW HERE
}, 10000)
Test code tried:
// from inside the setInterval/setTimeOut function above
// if $(this)
if($(this).length !== 0) {
alert('hello');
} // alerts hello
// from inside the setInterval/setTimeOut function above
// if html-tag (body/head/html)
if($('body').length !== 0) {
alert('hello');
} // alerts hello
// from inside the setInterval/setTimeOut function above
// if className
if($('.className').length !== 0) {
alert('hello');
} // alerts hello
// from inside the setInterval/setTimeOut function above
// if id
if($('#id').length !== 0) {
alert('hello');
} // alerts hello
So it finds specific elements inside the document just fine and apart from $(this) you can then traverse the DOM from there
ie.
// from inside the setInterval/setTimeOut function above
if ($('.className').find(something).length !== 0)... // works just fine
The problem comes when you want to use $(this) as a starting point for traversing
Traversing test code tried using $(this) as a starting point:
// from inside the setInterval/setTimeOut function above
// traversing up the DOM
if($(this).parents('body').length !== 0) {
alert('hello');
} // no alert (same with closest/parentsUntil/etc up the DOM)
// from inside the setInterval/setTimeOut function above
// traversing sideways in the DOM
if($(this).siblings('body').length !== 0) {
alert('hello');
} // no alert (same with next/prev/etc sideways in the DOM)
// from inside the setInterval/setTimeOut function above
// traversing down the DOM
if($(this).find('body').length !== 0) {
alert('hello');
} // no alert (same with children down the DOM)
So starting from $(this) 'body' (or anything else for that matter) doesn't seem to exist anywhere (up/down/sideways) which is not true from any other starting point from above
I also tried using a named global function as in:
// set outside/inside document.ready
function testCode() {
// INSERT EXAMPLE TRAVERSING TEST-CODE FROM ABOVE HERE
}
// set this code anywhere (inside/outside - document.ready/named-function/...) and run it
var autoTimer = setInterval(testCode, 10000) // no alert
// set this code anywhere (inside/outside - document.ready/named-function/...) and run it
var autoTimer = setInterval(function(){
testCode();
}, 10000) // no alert
So to re-iterate my question:
Why does it seem impossible in jQuery to traverse the DOM (up/down/sideways) inside a setInterval/setTimeOut using $(this) as a starting point?
And what are possible hacks/work-arounds/etc. for this behavior?
Possible none-solutions for hacks/work-arounds/etc.
this answer using $.proxy only works if you use an older version of jQuery - but since as this document attests 'This API has been deprecated in jQuery 3.3'... at the moment this is not a solution.
And neither is the bind method since as the document attests 'As of jQuery 3.0, .bind() has been deprecated' and been superseded by the on method (but I can't see how I would use .on() here to any effect - but maybe that's just due to my lack of imagination).
This is a basic this
scoping problem and has little to do with jQuery.
The most straightforward approach is to assign this
to another variable outside of the callback:
var that = this;
setInterval(function () {
$(that).parents();
}, 1000);
If you don't care about IE, or if you have a build process, you can use an arrow function, which uses this
from the scope outside it:
setInterval(() => {
$(this).parents();
}, 1000);
My personal preference is to avoid the use of this
entirely because of all its cockamamie behavior that's giving you so much grief. It's not clear from your examples where your this
value is coming from, but here are some ways you can avoid it:
// inside .each()
$('a').each(function (_, el) {
$(el).parents();
});
// inside event handler
$('a').on('click', function (event) {
$(event.target).parents();
});