why $(':not(:has(*))').find("p"); won't give an output - javascript

I wanted to get the parent node of a text, and I know that it's not easily possible and should be done with manual traversing. but I want to know why the following is not working.
$(':not(:has(*))').find(':contains("mytext")');
I made it easier with just looking for p tag in the result.
I know $(':not(:has(*))'); would return some P tags
.find("p"); should select those P tags
I know it's not working but I want to know why?

You're trying to find descendant elements of elements with no descendants. That's not going to work.
If you're looking for p elements with no descendants, you probably meant to use .filter(), not .find():
$(':not(:has(*))').filter('p')
Or you can just attach the p selector to the :not() — there really is no reason to run a separate selector here (unless your selector string is coming from a variable or something):
$('p:not(:has(*))')

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 > *"

Multiple not() DOM Selectors

I want to select a particular node with two not clauses, but I had no success so far. What I need to do is, select an element whose div contains the string 0008, but it's not 10008 and also it does not contain the tag "style", so, in theory it should work like that:
document.querySelectorAll(" div[id*='0008']:not([id='10008'][style])")
However, as you might suspect, it doesn't work that way.
document.querySelectorAll(" div[id*='0008']:not([id='10008'])")
document.querySelectorAll(" div[id*='0008']:not([style])")
Both of them work perfectly individually, of course.
not 10008 and also it does not …
That's not what your current selector checks, it test whether it has not ( the id and a style attribute ) . Use this instead:
div[id*='0008']:not([id='10008']):not([style])
Your original solution also was not a valid selector, since :not() may only contain one simple selector, while you had two of them. Yet, selector libraries like jQuery's sizzle engine might support them. So with jQuery, the following would work as well:
div[id*='0008']:not([id='10008'],[style])
jsFiddle Demo
Logically, you are trying to exclude elements that match either of the two undesired selectors, not elements that match them both. In jQuery, the multiple selector (which will then match all of the undesired elements, then be negated) is simply a comma-separated listing. Therefore you simply do this:
$("div[id*='0008']:not([id='10008'],[style])")
From the jQuery docs (since this question is tagged jQuery):
All selectors are accepted inside :not(), for example: :not(div a) and :not(div,a).
I'd just do:
var elems = $('div[id*="0008"]').filter(function() {
return !this.hasAttribute("style") && this.id == '10008';
});
I don't think I really get this, but this would filter out:
<div id="10008" style="color: black">10008</div>
but not:
<div id="10008">10008</div>
ID's are of course unique, and there could be a real world use case for this, but it still seems like an edge case that you'd normally handle some other way, as once you'd filtered out the ID, why exactly do you need to match a style tag as well ?

JavaScript String .replace method is replacing all my content?

I have successfully implemented finding and replacing some text with something else in the following way:
$(".class").html($(".class").html().replace(/\text\b/g, '<span class="newclass newclass2">new text</span>'));
When I apply this to my element 'class' it finds all the 'text' and replaces with 'new text' and everything relating to the new classes.
However, if I have more than one element on the page with the same class, it replaces all the classes with whatever text is in the first class.
For example, if my first class has the content "Hello everyone", when the script is applied to this class, it works fine. Any subsequent class of the same name is then replaced with "Hello everyone". These also have the function applied in the same way as the first occurrence of that class.
IE, it applies the script, then replicates this in every single class of the same name on the page.
I do not understand why it would do this, and rather renders the function pointless in many ways if it can't be used to change text throughout different sections without setting up new scripts and different classes.
Hopefully there is something simple at work here that I am not aware of, any help would be much appreciated.
Many thanks
Richard
That is the nature of class selectors--the .html(...) will replace the HTML of everything that matches the .class selector.
If you want to replace text in each individual .class element, you can use the .each function. (There are probably jQuerier ways, too.)
$(`.class`).each(function(n, el) {
var myHtml = $(this).html();
myHtml = mungeIt(myHtml);
$(this).html(myHtml);
});
If you want to select only an individual .class element, then you either (a) don't really want to be using classes, but IDs, or (b) need to understand enough of your structure or the context you wish to operate in to select only the targeted DOM element.
(And hope the structure or context doesn't change without a corresponding code update.)
You're specifying a class with the jQuery selector $(".class") That's what the period indicates. jQuery has a ton of selectors to choose from. A list is provided in the documentation here: http://api.jquery.com/category/selectors/
Also, I'd look at http://api.jquery.com/hasClass/ for your problem as you could then use if...then statements to not run into others
Dave is right about needing to use the .each method. We need to loop through each element at a time because .html() will only return the first element when there are multiple matches.
Try:
$('.class').each(function() {
$(this).html($(this).html().replace(/someWord/g,'withAnother'));
});

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.

Help me with my selector, the ID is dynamically changing every page load

I want to scan a website using jQuery, but the ID is constantly changing, but there's a permanent pattern for the ID that I'm searching for:
app7019261521_the_coinb4678bc2
app7019261521_the_coind42fgr23
app7019261521_the_coing0992gvb
app7019261521_the_coin12e5d0aa
The IDs always starts with app7019261521_the_coin
But my problem is I don't know how to put that in jQuery selector.
$("#app7019261521_the_coin")
Doesn't seem to work
So how can I make this work?
$("[id^=app7019261521_the_coin]")
Should work - but its MUCH slower selector than knowing the real ID - or assigning a class. This selector will scan every element on the page one at a time, there is no good way for this selector to be optimizied. 9 times out of 10 though you could build a better selector: Is this #app7019... element the direct child of another element that is easier to determine? like a id='container'?
$("#conainter > [id^=app7019261521_the_coin]"); for instance
From the jQuery Selector Documentation
[attribute^=value] Returns: Array<Element(s)>
Matches elements that have the specified attribute and it starts
with a certain value.
can you set a class and just call it by a class name?
you may also be able to try
$("div[id^=app7019261521_the_coin]")
This will find all div's that start with app7019261521_the_coin
Replace div with whatever element type you are searching for.
$j('div[id^=app7019261521_the_coin]')
Remember this is not very optimal, as it causes the script to check the id attribute of every matched element.
You might want to see how you can add a class to the element or at least find the parent element first and traverse from there.

Categories