Enjoy!
<html>
<head>
<script src="https://code.jquery.com/jquery-4.0.0-beta.2.js"></script>
</head>
<body>
<ul id="list">
<li>foo</li>
<li>bar</li>
<li>gnord</li>
</ul>
<script>
(function ($) {
"use strict";
if ("development") {
// jQuery strict mode, logs errors on empty selectors if not opting out
const getStackTrace = function (error) {
const stack = error.stack || '';
return stack
.split('\n')
.map(function (line) {
return line.trim();
})
.filter(function (line) {
return !!line;
});
};
// by calling try() we silence selectors that returns empty
// problem is that this function runs after the selector
$.fn.try = function () {
this.__store.try = true;
return this;
};
// thus we use the GC to check when the jQuery object is destroyed
const registry = new FinalizationRegistry((store) => {
if (!store.try) {
console.error(
'Empty result for selector "' + store.selector + '"',
getStackTrace(store.error)
);
}
});
// override the init method
const jQueryInit = $.fn.init;
$.fn.init = function (selector, context) {
const result = new jQueryInit(selector, context);
if (selector && result.length === 0) {
const store = {selector: selector, try: false, error: new Error()};
result.__store = store;
registry.register(result, store);
}
return result;
};
} else {
$.fn.try = function () {
return this;
};
}
})(jQuery);
// normal usage, have result, no error
$("#list li").each(function (i, el) {
console.log(el);
});
// empty result, triggers error log
$("#nolist li").each(function (i, el) {
console.log(el);
});
// empty result but with try, no error
$("#trylist li").try().each(function (i, el) {
console.log(el);
});
</script>
</body>
</html>