0

I need to execute a function on an element that may or may not exist in the DOM, using a querySelector (not querySelectorAll, because there will be at most one such element). The most elegant solution I can devise is:

(function (element) {
 if (element) {
 [business logic of the function]
 }
}(document.querySelector([css selector])));

The goal would be not to call the function at all if the querySelector doesn't return an element. (querySelector returns null if no matching element is found.)

My original question lacked clear criteria, and my objections to the responses resulted in the question being closed as being opinion-based. Here's the criteria:

I want to solve this without having to declare a variable external to the routine, e.g., not:

let x = document.querySelector([css selector]);
if (x) {
 [business logic on x]
};

I'd like something as elegant as document.querySelectorAll, which returns an iterable NodeList (or null) that does not require testing on the returned data before performing work on it. I.e.,

document.querySelectorAll([css selector]).forEach(function (element) {
 [business logic using element]
});

querySelectorAll is overkill here, because I'll have 1 returned element at most, and if nothing is returned, no business logic will be run.

At present, the only way I can see to do it is: pass the returned value to the function, and check in the function whether the return is not falsy before proceeding with the work of the function.

Is there an elegant way not to call the function if the return of the querySelector is not an element?

Again, I don't want to use querySelectorAll, since there will be at most one element that matches the querySelector. The beauty of the current solution is that it relies on an anonymous function, and there's no need to create an explicit variable (with var or let), since the function provides a param for passing.

Mark Rotteveel
110k239 gold badges158 silver badges230 bronze badges
asked Oct 5, 2023 at 23:08
23
  • 1
    Depends on the business logic. If it is a single method call on that element you can use document.querySelector(...)?.someMethod(...). Commented Oct 5, 2023 at 23:41
  • 1
    @AfzalK. doesn't say that anywhere in the question Commented Oct 6, 2023 at 2:08
  • 1
    Note that querySelectorAll() solves this problem, but it's overkill. With it, you can do document.querySelectorAll([selector]).forEach((element) => { [business logic] }); If nothing is returned, the function is not invoked. I'm essentially seeking the same kind of elegance for querySelector. Commented Oct 6, 2023 at 19:07
  • 2
    @Tom, I think this looks really goofy but it fulfills your requirement of not using querySelectorAll: [document.querySelector([selector])].filter(Boolean).forEach(function(element) { [business logic] }) :) Commented Oct 6, 2023 at 19:11
  • 1
    @Tom, sure but it is simply impossible to somehow not invoke the function expression in an immediately invoked function expression. Commented Oct 6, 2023 at 21:20

3 Answers 3

1

You can assign the variable in the if statement.

let element;
if (element = document.querySelector("selector")) {
 // do something with element
}
answered Oct 5, 2023 at 23:22

5 Comments

This is shorter, indeed, but I'd like not to have to define 'element' explicitly, though.
There's nothing better than your solution then.
Unlike for, you can't put the variable declaration directly in the if condition.
@Barmar So for (let element = document.querySelector("selector"); element; element = null) { ... }? shudder
@Bergi Ugh, that's right up there with switch(true) { case condition1: ... case condition2: ... }
1

I don't want to use querySelectorAll, since there will be at most one element that matches the selector

You may want to reconsider this stance. This is a perfect use case for querySelectorAll: you want to get a collection with all the elements in the document that match the selector. If your document contains at most one such element, that's ok, and you'll get a collection of either 0 or 1 elements - that doesn't change the fact that this is just a special case of getting N elements.

Using this approach, the code is short and simple:

document.querySelectorAll(...).forEach(element => {
 ... // business logic
});

or

for (const element of document.querySelectorAll(...)) {
 ... // business logic
}

If it would look weird to have the logic executed multiple times, add a comment to explain that the selector matches either 0 or 1 elements.


That said, I don't see anything wrong with

(element => {
 if (!element) return;
 ... // business logic
})(document.querySelector(...));

If you really need a special syntax for this, write your own helper function or have a look at discussions for some pipeline operators, which proposed optional pipelining like

document.querySelector(...) ?> (element => {
 ... // business logic
});

You would need to write your own transpiler plugin or macro for that, though.

answered Oct 7, 2023 at 3:23

1 Comment

I've used querySelectorAll() as you've described for years, for cases where there will be many matching elements, but I've always cringed when using it for cases where I simply need to grab one element & yet safeguard against the edge case where that element may not exist. I'd like to use a shorter/distinct routine for cases where I know only one such element should exist; among other benefits, using different routines will therefore be a visual indicator of what the routine is expecting.
0

The method that Đinh-carabus proposed in the comments above is likely closest to the solution I'm seeking, but only if abstracted into a function that can be called as needed, to wit:

function myQuerySelector(selector, func) {
 Array.from(document.querySelector(selector)).filter(Boolean).forEach(func);
}
// trivial use case:
myQuerySelector('body', function (element) {
 console.log(element);
});

A less complex solution that could be used without calling an original function would be preferable, but this solution may have general value, so I'm posting it here as a solution.

answered Oct 7, 2023 at 2:17

5 Comments

Well if you're going to use a helper function anyway, you may as well use a normal if statement in there instead of this hard-to-understand syntax.
heres a more short version: [...document.querySelectorAll('div')].forEach(function(element) { console.log(element); });
Afzal K., I want to avoid querySelectorAll() in this case.
@AfzalK. Also, what you've proposed is the same as document.querySelectorAll('div').forEach(function (element) { console.log(element); }); There's no need to spread the results of querySelectorAll, as it returns an iterable nodelist that .forEach can be called on directly.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.