How to get the second match with QuerySelector? - javascript

The following statement gives me the first element with the class titanic
element = document.querySelector('.titanic');
How would I retrieve the second element with the same class?

Use document.querySelectorAll
document.querySelectorAll('.titanic')[1]

You don't necessarily need querySelectorAll for picking second element and the question is to use querySelector API. You can utilizing the power of CSS in the selector.
For example you can do:
document.querySelector('.titanic:nth-child(2)')
to pick second element. NOTE: the count starts at 1, not 0.
Refer to this quick CodePen to play around this approach:
https://codepen.io/adamchenwei/pen/RwZvQvW?editors=1111
NOTE: It is not accurate to use n-th-child against classes among child elements that have different class names. Plz do not do that. I would suggest to find alternative method (which I am not aware at this time)
ref: nth-child doesn't respond to class
a use case example WILL NOT WORK with n-th:child: https://codepen.io/adamchenwei/pen/MWQMObL

Related

querySelector() - first inner element

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.
: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.)
"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 > *"

Accessing the 2nd member of a class in jQuery?

I have a stack of divs that belong to a specific class, say tabs. and the semantic structure looks something like this:
<div class = "tabs" >_______</div>
<div class = "tabs" >_______</div>
<div class = "tabs" >_____</div>
It's easy to access the first and the last element of the div like
$('.tabs:first') or
$('.tabs:last')
but getting to the 2nd (and assuming there are multiple other divs inside then the all divs other than first or last) seems to yield a syntax error to me such as :
$('.tabs:second') or $('.tabs:third') do not work expectedly.
Can anyone pinpoint what is wrong here?
Try using the eq() selector, notice the index is zero-based:
$(".tabs:eq(1)");
There are two ways to do this, either using the .eq() method or the :eq() selector.
The .eq() method:
The way the jQuery documentation recommend you do this is by making use of the .eq() method.
// eq() is zero-based, so this would get the second element
$('.tabs').eq(1)
A handy feature is that .eq() also can take a negative number, which causes the function to start from the end instead.
// This would take the second element from the end
$('.tabs').eq(-2);
The :eq() selector
jQuery also provide an :eq() selector, that basically work the same way the .eq() method does. So you could do this as well:
$('.tabs:eq(1)')
Notice that even though this work, it is preferred to use the .eq() method instead of the selector. The method has better performance in modern browsers and the :eq() selector does not support negative numbers, so it is somewhat more limited.
Take a look at the jQuery :first selector documentation:
http://api.jquery.com/first-selector/
You'll notice that this is a jQuery extension and not part of the CSS specification.
Also that :first is equivalent to :eq(0), that means if you want to get the second element, you can do it with :eq(1).
If you don't need a filter in the CSS selector, you can simply get the element with the .eq method like this:
$('.tabs').eq(0) // get the first element
$('.tabs').eq(1) // get the second element
$('.tabs').eq(2) // get the third element
$('.tabs').eq(-2) // get the second to last element
$('.tabs').eq(-1) // get the last element

Will jQuery search for the ID before filtering other parameters in the selector?

This question is related to performance.
If I use a selector like the following
$('#myID a') // Does this find #myID and filter by a?
Or should I write the statement like this?
$('#myID').find('a')
I'm not sure if jQuery is smart enough to execute this statement using the ID first or if it operates exactly like CSS and reads right to left. It's not such a big deal using tags but when you run something like
$('#myID .myClass')
It makes a HUGE difference in performance.
From a NetTuts article: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-think-right-to-left-with-jquery/
As an example, if Sizzle comes across a selector like $('#box p'),
it’s true that it works right-to-left, but there’s also a quick regex
optimization that will first determine whether the first section of
the selector is an id. If so, it’ll use that as the context, when
searching for the paragraph tags.
Relevant comment from SizzleJS:
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
When an Id is in the selector. jQuery will first execute document.getElementById then begin filtering for child elements.
basically this is why it is never a great idea to use just attribute or class selectors $('.someclass') or $('[name=myname]') without being more specific. Because it causes the code to traverse the DOM and look at every element to find that class.
By just adding a tagname to the same selector $('div.someclass') or $('div.[name=myname]') you improve efficiency becuase it will first run. document.getElementsByTagName narrowing the number of elements to search.

Using jQuery to delete all elements with a given id

I have a form with several spans with id="myid". I'd like to be able to remove all elements with this id from the DOM, and I think jQuery is the best way to do it. I figured out how to use the $.remove() method to remove one instance of this id, by simply doing:
$('#myid').remove()
but of course that only removes the first instance of myid. How do I iterate over ALL instances of myid and remove them all? I thought the jQuery $.each() method might be the way, but I can't figure out the syntax to iterate over all instances of myid and remove them all.
If there's a clean way to do this with regular JS (not using jQuery) I'm open to that too. Maybe the problem is that id's are supposed to be unique (i.e. you're not supposed to have multiple elements with id="myid")?
.remove() should remove all of them. I think the problem is that you're using an ID. There's only supposed to be one HTML element with a particular ID on the page, so jQuery is optimizing and not searching for them all. Use a class instead.
All your elements should have a unique IDs, so there should not be more than one element with #myid
An "id" is a unique identifier. Each time this attribute is used in a document it must have a different value. If you are using this attribute as a hook for style sheets it may be more appropriate to use classes (which group elements) than id (which are used to identify exactly one element).
Neverthless, try this:
$("span[id=myid]").remove();
id of DOM element shout be unique. Use class instead (<span class='myclass'>).
To remove all span with this class:
$('.myclass').remove()
if you want to remove all elements with matching ID parts, for example:
<span id='myID_123'>
<span id='myID_456'>
<span id='myID_789'>
try this:
$("span[id*=myID]").remove();
don't forget the '*' - this will remove them all at once - cheers
Working Demo
The cleanest way to do it is by using html5 selectors api, specifically querySelectorAll().
var contentToRemove = document.querySelectorAll("#myid");
$(contentToRemove).remove();
The querySelectorAll() function returns an array of dom elements matching a specific id. Once you have assigned the returned array to a var, then you can pass it as an argument to jquery remove().
You should be using a class for multiple elements as an id is meant to be only a single element. To answer your question on the .each() syntax though, this is what it would look like:
$('#myID').each(function() {
$(this).remove();
});
Official jQuery documentation here.
As already said, only one element can have a specific ID. Use classes instead. Here is jQuery-free version to remove the nodes:
var form = document.getElementById('your-form-id');
var spans = form.getElementsByTagName('span');
for(var i = spans.length; i--;) {
var span = spans[i];
if(span.className.match(/\btheclass\b/)) {
span.parentNode.removeChild(span);
}
}
getElementsByTagName is the most cross-browser-compatible method that can be used here. getElementsByClassName would be much better, but is not supported by Internet Explorer <= IE 8.
Working Demo

jquery selector help

i have a event i trigger on every link in my site.
but then i want it to NOT trigger on links i've got class='nofocus' on.
for example
<a>link</a>
<a class='nofocus'>register</a>
$('a').live('click', function() {
$('#searchbox').focus();
}
how do i rewrite the $('a) so the second link wont trigger the event?
Theoretically, there are three options.
1. Using the “attribute not equal to” selector
The “attribute not equal to” selector matches elements that either don’t have the specified attribute or do have the specified attribute but not with a certain value.
$('a[class!=nofocus]')
This will only work as long as you don’t use multiple classes on your A elements in your markup, e.g. foo.
See Selectors/attributeNotEqual in the jQuery docs for more information.
2. Using .not()
Another option is to select all the A elements first, then filter the results, removing elements with class="nofocus" from the result set.
$('a').not('.nofocus')
This is more flexible, because it allows the use of multiple classes on A elements.
Also, it’s slightly faster than using The Attribute Not Equal To Selector™ in Firefox, but slightly slower in Safari.
See Traversing/not in the jQuery docs for more information.
3. Using the :not() selector
The fastest (and shortest) option is to use the :not selector:
$('a:not(.nofocus)')
Also, my tests point out that this is by far the fastest method of the three — more than twice as fast as using the attribute not equal to selector!
Performance comparison of the three options
I created a jsPerf test case so you can test this yourself: http://jsperf.com/get-elements-without-specific-class.
TL;DR: Use $('a:not(.nofocus)').
Try the :not() selector (docs):
$('a:not(.nofocus)').live('click', function() {
$('#searchbox').focus();
}
Selector: $("a:not(.nofocus)") will select all links without the nofocus class.
Use $("a:first"). http://docs.jquery.com/Selectors/first to just get the first one.
$('a:not(.nofocus)')
should do the job
http://docs.jquery.com/Selectors/not

Categories