Selector ignoring elements contained in a specific element - javascript

I am currently working on a software plugin which scans a page for links in order to edit them. But there is a problem: I dont want to edit links that are contained in a specific element (in this case: an edit box). The elements contained in this edit box can also be nested, so parent might not be appropriate.
Is there any way to exclude elements via selector that are contained in a specific element?

You can run this plain JavaScript, it returns all elements with the matching pattern not in the container you specify.
var anchors = document.querySelectorAll('*:not(.editBox)>a.link');
Assuming your not wanted container has a class of "editBox" and you can change the matching "link" class to be any query selector you want, can be a plain 'a' for all anchor elements. I created a JSFiddle as a demo.

This doesn't all have to be on one selector. You could very simply use your regular selector to catch all the elements, and then execute a not() function to trim down the elements to only those you need.
var elems = $( "a" ); // all anchor links
elems = elems.not( ".ignore_me" ); // remove all links with the "ignore_me" class.
You could even combine these two into one command using function chaining:
var elems = $( "a" ).not( ".ignore_me" );
A third option that I feel is a little less readable would be something like this:
var elems = $( "a:not( .ignore_me )" );
References:
:not()
not()

Related

Create new element from selector?

How can I create a new element using just a selector? (e.g. .myclass, #myid or a:not(.someclass)) Basically there is no way for you to tell if the element is a div, span, li, an anchor, if it's a div with a class or with an id and so on.
In jQuery I know you can do $(selector) to get a usable DOM object. But how can this be done in JavaScript?
In jQuery I know you can do $(selector) to get a usable DOM object...
Not to create one. jQuery will do a search in the DOM for existing matches. You can do $("<div>") and such (note that's HTML, not a CSS selector) to create elements, but jQuery doesn't have a feature for creating elements from CSS selectors.
But how can this be done in JavaScript?
You'll have to parse the selector, and then use document.createElement with the tag name, and then set any classes or other things the selector describes on the new element.
CSS selectors aren't very hard to parse. You'll be able to find a lib that does it. (jQuery has Sizzle, which is a selector engine, built in and Sizzle is open source. It will naturally have code to parse selectors.)
Mootools does this.
new Element('#name.class')
yields
<div id=​"name" class=​"class">​</div>​
The answer appears to be that there is no built-in way of doing this. Maybe there’s a library which does the job.
However, it’s not to hard to write a function to create an element from a simple selector:
/* createElementFromSelector(selector)
================================================
Usage: element#id.class#attribute=value
================================================ */
function createElementFromSelector(selector) {
var pattern = /^(.*?)(?:#(.*?))?(?:\.(.*?))?(?:#(.*?)(?:=(.*?))?)?$/;
var matches = selector.match(pattern);
var element = document.createElement(matches[1]||'div');
if(matches[2]) element.id = matches[2];
if(matches[3]) element.className = matches[3];
if(matches[4]) element.setAttribute(matches[4],matches[5]||'');
return element;
}
var testitems = [
'div#id.class#attribute=value',
'div#id.class#attribute',
'div',
'div#id',
'div.class',
'#id',
'.class',
'#id.class',
'#whatever'
];
testitems.forEach(item => {
var element = createElementFromSelector(item);
console.log(element);
});
The tricky part is the regular expression. You can see it in detail here: https://regex101.com/r/ASREb0/1 .
The function only accepts selectors in the form element#id.class#attribute=value with the any of components being optional, as you see in the test items. I think including pseudo classes is probably pushing the friendship, but you might like to modify it to include multiple real classes.

displaying specific text on tumblr [duplicate]

It looks like JQuery does the search in the current document when using a selector.
How to search for an element only inside a div element?
jQuery selectors work very much like CSS selectors, which you may be more familiar with.
First, select the div, and then descend from that:
$('#my-div').find('some-selector').
or build your selector to match children of the element in question:
$('#my-div some-selector')
Old question, but everyone seems to have missed the scoped jQuery selector (using the scope you desired, i.e. your div selector, as the second parameter)
e.g. use
var $matches = $('.adiv', '#mydiv');
This is a shorter equivalent of:
var $matches = $('#mydiv').find('.adiv');
var elems = jQuery(".foo", jQuery("#divYourWantToLimitTo") ); //BAD
//or
var elems = jQuery("#divYourWantToLimitTo .foo"); //Better
//or
var elems = jQuery("#divYourWantToLimitTo").find(".foo"); //BEST
jQuery provides several ways to search for specific elements:
$("#your_div").find(".your_things"); //Find everything inside
//-or-
$("#your_div").filter(".your_things"); //Find only the top level
//-or-
$("#your_div .your_things"); //Easiest
var elements = $('div ' + yourSearch);
$('div-selector').find('the selector-you-are-looking-for');

jQuery combine multiple elements

I was experimenting with jQuery and came across a question. Can I use an actual selector with existing element as a combined selector for jQuery?
So, suppose I've created a DIV element on-the-fly:
var $div = $('<div>')
.css('position', 'absolute')
.hide(); // Just to be short
$('body').append($div);
And I want to show it when user hovers over P elements / paragraphs (at the cursor position):
$('p').hover(function(e) {
// Change the position of $div with regards of cursor position
$div.show();
}, function(e) {
$div.hide();
});
BUT also I want to apply this hover handlers to $div itself. So instead of duplicating my code, I want to do something like this:
$('p', $div).hover(...)
which will select $div element along with all P elements.
I know I can write functions separately and pass the names as arguments to hover function.
Is there any solution to this kind of situation in jQuery? or is there a more accurate solution?
You could use the jQuery add method:
$('p').add($div).hover(function(e) { ...
If you have multiple elements to combine you don't wan to do
$('p').add($div1).add($div2).add($div3).add($div4).add($div5).add($div6) ...
Instead you want to convert a JavaScript array into a jQuery object
$( $.map([x,y,z], a => [...$.makeArray(a)]) )
Source: Merging jQuery objects

How to use jQuery selector/find() against text string without inserting into the DOM?

I have a text string i'm trying to select the spans from using jQuery. I'd like to grab the spans w/o adding the element to the dom -- if that's possible?
After reading the jquery docs i was under the assumption that i could create a fragment by wrapping the string in a jquery selector tag, then using.find() to find the elements i want.
I have code that is similar to this but from the looks of the last line, it's obvious that no spans are being selected; any help would be greatly appreciated:
// 'text' is normally generated automatically...
// just made it an escaped string for example purposes.
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $(text).find('span');
console.log(text); // => <span id="blah1">Y</span><br/><span id="blah2">o</span><br/>
console.log(spans.length); // => 0
Thanks.
You want to use filter(), not find()
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $(text).filter('span');
console.log(spans.length);
jsFiddle
From the jQuery docs
filter:
The supplied selector is tested against each element; all elements
matching the selector will be included in the result.
find:
the .find() method allows us to search through the descendants of
these elements in the DOM tree and construct a new jQuery object from
the matching elements.
with your html fragment, there is no wrapper element, so there is no descendants, hence why find() does not work.
You are basically doing:
var elems = jQuery("<span id=\"blah1\">Y</span>").add("<br/>").add("<span id=\"blah2\">o</span>").add("<br/>");
If you want find to work with find(), you need to wrap it in an element.
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = jQuery("<div></div>").append(text).find("span");
console.log(spans.length);
You want to use filter in this case:
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $(text).filter('span');
console.log(spans.length); // 2
Demo: http://jsfiddle.net/ambiguous/TGY3J/
Or wrap it in a <div> and use find:
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $('<div>' + text + '</div>').find('span');
console.log(spans.length); // 2
Demo: http://jsfiddle.net/ambiguous/qbCjk/
find works on descendants but without the <div> wrapper, your $(text) doesn't have any <span> descendants. Wrapping your HTML in a <div> is probably your best bet, that way you don't have to worry about how deep your desired elements are.

Selecting only one element in the jQuery collection

How do I limit an event to a single element in a jQuery collection?
In the case below, I've tried using .one() to limit the behaviour (inserting the <li class='close'>Close</li> line of HTML) to a single instance. The behaviour does indeed happen only once, but on EVERY matched element of $( "ul>li>a" ). How do I make it happen only once, to only ONE of the matched elements in the collection?
Any ideas?
$( "ul>li>a" ).one(
"click",
function(){
$( "ul ul")
.prepend("<li class='close'>Close</li>")
}
);
Thanks in advance.
-AS
A jQuery selection returns an array. Therefore $("selection")[0] can work. However there are better abstracted methods for this, like .get(0) or .first() (in case you're looking for the first element of the selection/array).
$("selection").get(index) returns the pure DOM element (at that specific index) of the selection, and is not wrapped in the jQuery object.
$("selection").first() returns the first element of the selection, and wraps it in a jQuery object.
So if you don't necessarely want to return the first element, but still want jQuery functionality, you can do $($("selection").get(index)).
Given your situation, this should work fine:
// bind the 'onclick' event only on the first element of the selection
$( "ul>li>a" ).first().click(function() {
$( "ul ul").prepend("<li class='close'>Close</li>");
});
Which is equivalent to this:
$($( "ul>li>a" ).get(0)).click(function() {
$( "ul ul").prepend("<li class='close'>Close</li>");
});
And this:
$($( "ul>li>a" )[0]).click(function() {
$( "ul ul").prepend("<li class='close'>Close</li>");
});
I must disagree with Ryan, working on the CSS selection string to filter the result is rather expensive compared to the native JavaScript array functionality.
Try first(), it selects the first element:
$( "ul>li>a" ).first().one('click',
function(){
$( "ul ul").prepend("<li class='close'>Close</li>")
}
);
one() is used, as you already noticed, to handle an event only once.
You have to specify the index of the element you want to work with.
If your selector returns more than one element you can do one of a couple things...
You can isolate your elements by giving them a class or id attribute in your html and alter the selector to select only the class/id of the element/s you wish to select or you can specify the index of the element you're trying to work with. The later method is a bit sloppy but works as long as your page structure doesn't ever change.
So for the first method I spoke of you'd change your selector to this after applying a class/id to your elements:
$("ul>li>a.class")
or
$("ul>li>a#id")
For the second method I mentioned you'd change your selector to this:
$("ul>li>a:eq(index)")
Where index is the zero based index of the element you're trying to select.
You can call the first method, which will return a new jQuery object containing only the first element in the original one.
However, in your case, you might as well use the (equivalent) :first selector, like this:
$("ul > li > a:first").click(function() { ... });
If you only want to handle the first click event and ignore any subsequent clicks, you'll need to use .one(), like you already are.
You need to combine first() with one():
$( "ul>li>a" ).first().one('click', function () {});
More general:
$( "ul>li>a:eq(n)" ).one('click', function () {});

Categories