3

I'm using SVG.js select() function which uses querySelector() function.

Currently, the command I use is: select("[id='1']") (1 could be replaced by some other number)

What I'd like to do is to select the first inner element inside this element. Alternatively, I could select it by tag name.

How to do it?

I tried select("[id='1']:first") but received an error.

By the way, the reason I select it like that is that apparently querySelector has a problem with id's which are numbers.

Elimination
  • 2,619
  • 4
  • 22
  • 38
  • 1
    No one knows why it happens anymore nowadays. "But it's provocative; gets people going!" I voted you back up to help. I strongly dislike it when some random person does that with no actual contribution. One theory is they want the badge that is awarded when you do this to people. – suchislife Jan 21 '18 at 17:08
  • 1
    numeric ids [are best avoided](https://stackoverflow.com/questions/70579/what-are-valid-values-for-the-id-attribute-in-html) – Robert Longson Jan 21 '18 at 17:09
  • 1
    @RobertLongson, for the time being, I cannot change the ids. – Elimination Jan 21 '18 at 17:10
  • 1
    Expect life to be hard then. [CSS expects identifiers not to start with a number](https://www.w3.org/TR/CSS21/syndata.html#characters) – Robert Longson Jan 21 '18 at 17:59

2 Answers2

8

:first is a jQuery thing. For what you're doing, you can use :first-child, which is a CSS thing:

select("[id='1'] > :first-child");

That selector matches all elements that are the first child of elements with id="1", but if select is using querySelector under the covers, you'll get the first such element.

Note that the > in that is the child combinator: It means we're looking for :first-child within [id='1']. (An earlier version of this answer used [id='1'] :first-child, which uses a descendant combinator [just whitespace]. It would matter for selecting a list of elements, but not if selecting only on the first match.) (You need one or the other, since without any combinator ([id='1']:first-child) it would b elooking for the first [id='1'] that was also a :first-child.)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Actually it doesn't work for me: `Uncaught DOMException: Failed to execute 'querySelectorAll' on 'Element': '[id='1'] : first-child' is not a valid selector.` – Elimination Jan 21 '18 at 17:10
  • 2
    @Elimination: Drop that space after the :. – BoltClock Jan 21 '18 at 17:11
  • 2
    @Elimination: As BoltClock says, `: first-child` is not the same as `:first-child`. Spaces are **significant** in CSS selectors (`div.foo` and `div .foo` and `div. foo` are all different as well, for instance; with the last being invalid). – T.J. Crowder Jan 21 '18 at 17:12
  • I would never guess it myself, it's kinda rare for spaces to be significant in a code. – Elimination Jan 21 '18 at 17:13
  • 1
    @Elimination: CSS is mildly unusual in that spaces *between* tokens are significant, but most languages are sensitive to spaces *within* tokens. Putting a space in `:first-child` isn't unlike putting a space in `10. 4`, which also breaks. In both cases, they're unexpected spaces within fundamental tokens of the language. But in any case, now you know: CSS is space-sensitive. :-) – T.J. Crowder Jan 21 '18 at 17:17
1

"I'm using SVG.js select() function which uses querySelector() function."

But your comment under TJ's answer suggests it uses querySelectorAll(). There's a difference.

"What I'd like to do is to select the first inner element inside this element."

If it does use querySelector, then use this selector:

"[id='1'] > *"

That'll give you the first child element inside the [id='1'] element.

But if it actually uses querySelectorAll, then using TJ's :first-child selector will work, but as he noted, you need to be aware that it will return all elements that are the first child of their parent.

You can use the > child selector to ensure just one.

"[id='1'] > :first-child"

"Alternatively, I could select it by tag name. How to do it?"

I don't know which element you're referring to, but in general, include the tag name if the selector is selecting on attribute or position. That'll greatly help the engine to narrow down the set of elements.

// querySelector       // querySelectorAll
"div[id='1'] > p" ... "div[id='1'] > :first-child"

"I tried select("[id='1']:first") but received an error."

As TJ noted, that's an invalid selector. jQuery's selector engine is non-conforming to the standards in several different ways. Keep your selectors pure as much as possible so that you don't get hooked on needless dependencies.

"By the way, the reason I select it like that is that apparently querySelector has a problem with id's which are numbers."

You can select by numbers if you escape the leading number.

"#\\1 > *"
  • Thanks for the detailed answer. As for `querySelectorAll()`, you're right but it doesn't make a difference for the syntax question. isn't it? In this respect, both functions are the same. – Elimination Jan 21 '18 at 18:02
  • @Elimination: No difference for the syntax, but for the result you get. Using TJ's answer, you will get multiple elements with `querySelectorAll` if there are elements nested more deeply than one level, but if you use the `>` selector, you'll guarantee just one match with `:first-child`, since an element can have only one *direct* descendant that is a `:first-child`. –  Jan 21 '18 at 18:04
  • ...here's a demo: https://jsfiddle.net/gyv656uj/ But with `querySelector`, it's a non-issue, since you'll get just a single element either way. So it makes a difference as far as the result and how much work it needs to do. –  Jan 21 '18 at 18:09