Find parent which contains specific CSS property - javascript

I need to find the closest parent that contains the following css property:
background-image: url(*here goes a link to a valid image*);
Finding it could probably be done via selecting all elements, filtering them in an array, then using the first/last element of the array, but I'd like to know if there's a faster way to select the closest parent that fulfills the requirement I mentioned before, like this:
<parent1 style="background-image:url(http://google.com/images/photo.png);">
<parent2 style="background-image:url('http://google.com/images/photo.png');">
<mydiv></mydiv>
</parent2>
</parent1>
I want the parent2 selected;
Keep in mind that I do not know the background url.

You can extend the jQuery selector to allow for this. And with your specific rules.
Something like this:
$.extend($.expr[':'], {
backgroundValid: function(a) {
//change the url for what you want
return $(a).css('background-image') === "url(http://stacksnippets.net/validurl)";
}
});
var test = $('.foo').closest('div:backgroundValid');
console.log(test) //prints bar
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="background-image:url('validurl');">
<div class="bar" style="background-image:url('validurl');">
<div class="foo"></div>
</div>
</div>

There is no need to filter all parents, just make it recursive like this jsfiddle
html
<div id="parent1" style="background-image:url(validurl);">
<div id="parent2" style="background-image:url(validurl);">
<div id="start"></div>
</div>
</div>
javascript
$(function() {
var cssKey = "background-image";
var element = $('#start');
var found = false;
while (!found) {
element = element.parent();
found = element.css(cssKey) == 'none' ? false : true;
if (element.is($('html'))) break;
}
if (found) {
console.log(element.attr('id'));
} else {
console.log('Not found');
}
});

Probably this can help you,
select the element
pick up all its parent
filter its parent by what you want!
var firstParent, URL_TO_MATCH = "url(HELLO WORLD)";
$('#foo').parents().filter(function() {
var isValid = $(this).css('background-image') == URL_TO_MATCH;
if(!firstParent && isValid) {
firstParent = $(this);
}
return isValid;
});

Related

Select element by tag/classname length

I'd like to select an element using javascript/jquery in Tampermonkey.
The class name and the tag of the elements are changing each time the page loads.
So I'd have to use some form of regex, but cant figure out how to do it.
This is how the html looks like:
<ivodo class="ivodo" ... </ivodo>
<ivodo class="ivodo" ... </ivodo>
<ivodo class="ivodo" ... </ivodo>
The tag always is the same as the classname.
It's always a 4/5 letter random "code"
I'm guessing it would be something like this:
$('[/^[a-z]{4,5}/}')
Could anyone please help me to get the right regexp?
You can't use regexp in selectors. You can pick some container and select its all elements and then filter them based on their class names. This probably won't be super fast, though.
I made a demo for you:
https://codepen.io/anon/pen/RZXdrL?editors=1010
html:
<div class="container">
<abc class="abc">abc</abc>
<abdef class="abdef">abdef</abdef>
<hdusf class="hdusf">hdusf</hdusf>
<ueff class="ueff">ueff</ueff>
<asdas class="asdas">asdas</asdas>
<asfg class="asfg">asfg</asfg>
<aasdasdbc class="aasdasdbc">aasdasdbc</aasdasdbc>
</div>
js (with jQuery):
const $elements = $('.container *').filter((index, element) => {
return (element.className.length === 5);
});
$elements.css('color', 'red');
The simplest way to do this would be to select those dynamic elements based on a fixed parent, for example:
$('#parent > *').each(function() {
// your logic here...
})
If the rules by which these tags are constructed are reliably as you state in the question, then you could select all elements then filter out those which are not of interest, for example :
var $elements = $('*').filter(function() {
return this.className.length === 5 && this.className.toUpperCase() === this.tagName.toUpperCase();
});
DEMO
Of course, you may want initially to select only the elements in some container(s). If so then replace '*' with a more specific selector :
var $elements = $('someSelector *').filter(function() {
return this.className.length === 5 && this.className.toUpperCase() === this.tagName.toUpperCase();
});
You can do this in vanilla JS
DEMO
Check the demo dev tools console
<body>
<things class="things">things</things>
<div class="stuff">this is not the DOM element you're looking for</div>
</body>
JS
// Grab the body children
var bodyChildren = document.getElementsByTagName("body")[0].children;
// Convert children to an array and filter out everything but the targets
var targets = [].filter.call(bodyChildren, function(el) {
var tagName = el.tagName.toLowerCase();
var classlistVal = el.classList.value.toLowerCase();
if (tagName === classlistVal) { return el; }
});
targets.forEach(function(el) {
// Do stuff
console.log(el)
})

div#d1 toggle on p#1 click - howTo?

I am trying to toggle a div when its name is clicked.
I have multiple coupls like that in my page, and I want it to work as
"when <p id= "d2"> is clicked => <div id="d2"> is toggled".
I tried those functions:
$(document).ready(function(){
$("p").click(function(){
$("div#" + $(this).attr('id')).toggle();
});
});
function rgt() {
//document.body.innerHTML = "";
var id = "d" + this.id;
var situation = document.getElementById(id).style.display;
if (situation == "none") {
situation = "block";
}
else {
situation = "none";
}
}
function showHide(theId) {
if (document.getElementById("d" + theId).style.display == "none") {
document.getElementById("d" + theId).style.display = "block";
}
else {
document.getElementById("d" + theId).style.display = "none";
}
}
I can't make it Work!!! Why is it?
the browser says:"no 'display' property for null"...
I will be more than happy to solve it with simple jquery
Ensure Your id Attributes Are Unique
Assuming that your id attributes are unique, which they are required to be per the specification:
The id attribute specifies its element's unique identifier (ID). The
value must be unique amongst all the IDs in the element's home subtree
and must contain at least one character. The value must not contain
any space characters.
You should consider renaming your id attributes to d{n} and your paragraphs to p{n} respectively as seen below :
<button id='p1'>p1</button> <button id='p2'>p2</button> <button id='p3'>p3</button>
<div id='d1'><pre>d1</pre></div>
<div id='d2'><pre>d2</pre></div>
<div id='d3'><pre>d3</pre></div>
which would allow you to use the following function to handle your toggle operations :
$(function(){
// When an ID that starts with P is clicked
$('[id^="p"]').click(function(){
// Get the proper number for it
var id = parseInt($(this).attr('id').replace(/\D/g,''));
// Now that you have the ID, use it to toggle the appropriate <div>
$('#d' + id).toggle();
})
});
Example Using Unique IDs
You can see an interactive example of this approach here and demonstrated below :
Consider Using data-* Attributes
HTML supports the use of data attributes that can be useful for targeting specific elements through jQuery and associating them to other actions. For instance, if you create an attribute on each of your "p" elements as follows :
<button data-toggles='d1'>p1</button>
<button data-toggles='d2'>p2</button>
<button data-toggles='d3'>p3</button>
and then simply change your jQuery to use those as selectors :
$(function(){
// When an element with a "toggles" attribute is clicked
$('[data-toggles]').click(function(){
// Then toggle its target
$('#' + $(this).data('toggles')).toggle();
});
});
Is this you are looking?
$("#p1").on("click", function() {
$("#d1").toggle();
});
js fiddle: https://jsfiddle.net/Jomet/09yehw9y/
jQuery(function($){
var $toggles = $('.divToggle');
var $togglables = $('.togglableDiv');
$toggles.on('click', function(){
//get the div at the same index as the p, and toggle it
$togglables.eq($toggles.index(this)).toggle();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p class="divToggle">Show Me 1</p>
<p class="divToggle">Show Me 2</p>
<p class="divToggle">Show Me 3</p>
<div class="togglableDiv">Weeee 1</div>
<div class="togglableDiv">Weeee 2</div>
<div class="togglableDiv">Weeee 3</div>
Minimal approach using classes. This solution assumes the order of the p elements in the dom are in the same order as the divs are in the order. They do not have to be contiguous, but the order does matter with this solution.
ids are not the droids you are looking for.
An id needs to be unique. If you want to classify something one would suggest to use classes. You can actually use serveral of them for some fancy stuff. How about something like this:
<p class="toggle one">one</p>
<div class="toggle one" style="display:none">content one</div>
Straight forward. Every element that is a switch or switchable gets the class toggle. Each pair of switch and switchable(s) gets an additional identifier (like one, two, ...).
Simple JScript Implementation:
Now how about not using JQuery to work with that? Sure it i$ handy, but it hides all that neat stuff one would eventually like to learn her/himself!
var myToggle = {};
(function(module) {
"use strict";
(function init() {
var elements = document.getElementsByClassName("toggle");
var element;
var i = elements.length;
while (i) {
i -= 1;
element = elements[i].className;
elements[i].setAttribute("onclick", "myToggle.swap(\"" + element + "\")");
}
}());
module.swap = function(element) {
var couple = document.getElementsByClassName(element);
var i = couple.length;
while (i) {
i -= 1;
if (couple[i].style.display === "none" && couple[i].tagName === "DIV") {
couple[i].style.display = "block";
} else if (couple[i].tagName === "DIV") {
couple[i].style.display = "none";
}
}
};
}(myToggle));
<p class="toggle one">one</p>
<div class="toggle one" style="display:none">content one</div>
<p class="toggle two">two</p>
<div class="toggle two" style="display:none">content two 1</div>
<div class="toggle two" style="display:none">content two 2</div>
var myToggle = {} is the object we use to keep our little program contained. It prevents that our code conflicts with other declarations. Because what if some plugin on our site already declared a function called swap()? One would overwrite the other!
Using an object like this ensures that our version is now known as myToggle.swap()!
It may be hard to follow how it got to that name. Important hint: something looking like this... (function() { CODE } ()) ...is called an immediately-invoked function expression. iffy! It's a function that is immediatly executed and keeps its variables to itself. Or can give them to whatever you feed it in the last ()-pair.
Everything else is as verbose as can be... no fancy regular expressions, hacks or libraries. Get into it!

Find the first match after the particular element in rest of dom using jquery

Hi i have to find the "findme" div in dom structure after a particular div
and find the first instance after the div using one Jquery.
following may be the dom structure:
ex1:
<div class="abc"></div>
<div class="findme"></div>
ex2:
<div class="abc"></div>
<div>
<div class="findme"></div>
</div>
ex3:
<div>
<div class="abc"></div>
</div>
<div>
<div class="findme"></div>
</div>
ex4:
<div>
<div class="abc"></div>
</div>
<div class="findme"></div>
above are some examples of dom structure in which i want to find "findme" first div after "abc" div using jquery
i tried using after(),closest().nextAll().first()
but i am not getting the query to match above all examples .
so please can anyone help me to find the div first element ("finme") after "abc" div
I want find the next "findme" div in complete dom after "abc" div and first occurrence of after "abc" div above are some examples the dom can be
The only way I could find to get this to work for all cases is to walk the entire DOM sequentially with a filter:
var foundAbc;
$('*').filter(function() {
if ($(this).hasClass('abc')) {
foundAbc = true;
} else {
if (foundAbc && $(this).hasClass('findme')) {
foundAbc = false;
return true;
}
}
return false;
}).css('color','red');
JSFiddle: https://jsfiddle.net/TrueBlueAussie/LnLf79od/2/
You could wrap this behavior in a jQuery extension method if you wanted to make it reusable.
$.findNext = function(selector1, selector2) {
var foundAbc;
return $('*').filter(function() {
if ($(this).is(selector1)) {
foundAbc = true;
} else {
if (foundAbc && $(this).is(selector2)) {
foundAbc = false;
return true;
}
}
return false;
});
}
and use like this:
$.findNext('.abc', '.findme').css('color', 'red');
JSFiddle: https://jsfiddle.net/TrueBlueAussie/LnLf79od/3/

jQuery function similar to closest that will return elements outside of the parent chain

Is there any jQuery function similar to closest() that will return elements outside of the parent chain, traversing sideways? For example, I want to call a function foo() on the div source that would return the div target. I know I could navigate using parent() and siblings(), but I need something generic that would go as many levels as needed, up, sideways and down?
var allsources = $('.source');
allsources.click(function()){
$(this).closest('.target').hide();
});
<div class="row">
<div>
<div class="target" ></div>
</div>
<div>
<div>
<div class="source"></div>
</div>
</div>
</div>
<div class="row">
<div>
<div class="target" ></div>
</div>
<div>
<div>
<div class="source"></div>
</div>
</div>
</div>
EDIT:
My definition of closest: you have an element source. Try to find it down. If find more than one, return one that is less node hoops down/next/prev. If not found, go one level up, and try to find again. Repeat until no parent.
If, by closest, you mean "travel up as little as possible, then anywhere downwards", then you can do
$("#source")
.closest(":has(.target)")
.find(".target:first") //make sure we only select one element in case of a tie
In your case, it would be better to specify the common parent directly:
$(this)
.closest(".row")
.find(".target") //there's no tie here, no need to arbitrate
This is a tricky one. As has been commented, how do you define closest in this context? Assuming you can decide on some rules; for example:
Traverse up: 3pt
Traverse down: 2pts
Move sideways: 1pts
And then consider the item with the lowest points to be "closest" then it would be easy enough to author a plugin, named something such as closestAll, which would do the recursive traversal of the whole dom tree to determine the closest item.
However, looking at your recent edit, one (of many!) right solutions to the problem stated is:
var allsources = $('.source');
allsources.click(function(){
$(this).parents('.row').find('.target').hide();
});
Live example: http://jsfiddle.net/zCvJM/ (Source A only hides Target A, Same for B)
If you know exactly the structure of the dom and level of nesting, have you consider to use the eq() method
$(this).parents().eq(1).prev().children(".target")
I don't think there is a way to do this other than basically querying the whole DOM:
$('#target')
Because if you want to go up and across (never mind down as well) then the target element isn't related to the child element. If you also want to check for the presence of the child element you will have to do that separately.
-Edit:
After reading your comment on wanting to find the closest element regardless of whether it is a parent, I think you will have to write a custom function to crawl back up the dom one node at a time. I have tested the following and it works:
Markup
<div id="parent">
<div id="child1">
<div id="source"></div>
</div>
<div id="child2">
<div class="target" rel="right"></div>
</div>
<div id="child3">
<div>
<div class="target" rel="wrong"></div>
</div>
</div>
</div>
Script
$(document).ready(function () {
var tgt = findClosest($('#source'), '.target');
if (tgt != undefined) {
alert(tgt.attr('rel'));
}
});
function findClosest(source, targetSel) {
var crawledNodes = $();
var target = null;
// Go up
source.parents().each(function () {
console.log(crawledNodes.index($(this)));
if (crawledNodes.index($(this)) == -1 && target == null) {
crawledNodes.add($(this));
target = findTarget($(this), targetSel);
// Go across
$(this).siblings().each(function () {
console.log("Sibling");
if (crawledNodes.index($(this)) == -1 && target == null) {
crawledNodes.add($(this));
target = findTarget($(this), targetSel);
}
});
}
});
return target;
}
function findTarget(el, targetSel) {
console.log(targetSel);
var target = el.find(targetSel);
if (target.size() > 0) {
return target.eq(0);
}
else
{
return null;
}
}
If I understood the specification correctly you mean something like the function closest defined below:
var allsources = $(".source");
function closest($source,selector) {
if($source == null) return $([]);
var $matchingChildren = $source.find(selector);
if($matchingChildren.length != 0) return $($matchingChildren.get(0));
else return closest($source.parent(), selector)
}
allsources.click(closest($(this),'.target').hide();});
You can see it working at http://jsfiddle.net/y2wJV/1/
Your definition requires that when choosing among matching children the function must return one that is less node hoops down/next/prev. This requirement has not been met, but this function is quite flexible and seems to do what you want to do in the case of the example you provided.
I found this code that is simple but does not solve the tie issue (returns the first)...
(function ($) {
$.fn.findClosest = function (filter) {
var $found = $(),
$currentSet = this; // Current place
while ($currentSet.length) {
$found = $currentSet.find(filter);
if ($found.length) break; // At least one match: break loop
// Get all children of the current set
$currentSet = $currentSet.parent();
}
return $found.first(); // Return first match of the collection
};
})(jQuery);
I encountered a similar problem, i had a table i needed to find the next element which may be outside the current td, so i made a jquery function:
$.fn.nextAllLevels = function(sel) {
if ($(this).nextAll(sel).length != 0) {
return $(this).nextAll(sel).eq(0);
} else if ($(this).nextAll(':has(' + sel + ')').length != 0) {
return $(this).nextAll(':has(' + sel + ')').find(sel).eq(0);
} else {
return $(this).parent().nextAllLevels(sel);
}
So to use this you simply call
$('#current').nextAllLevels('.target');
To give you the element closest in the foward direction, regardsless of whether in is in the current parent or not.

jquery get certain class name of element which has several classes assigned

I need to read elements class name. I have elements like this:
<article class="active clrone moreclass">Article x</article>
<article class="active clrtwo moreclass">Article y</article>
<article class="active clrthree moreclass moreclass">Article z</article>
<article class="active clrone moreclass">Article xyza</article>
I need to parse out class name that starts with clr. So if second element was clicked then I would need to get clrtwo className.
You can use a regular expression match on the class name of the clicked item to find the class that begins with "clr" like this:
$("article").click(function() {
var matches = this.className.match(/\bclr[^\s]+\b/);
if (matches) {
// matches[0] is clrone or clrtwo, etc...
}
});
Here is solution for you:
$('article').click(function () {
var className = this.className.split(' ');
for (var i = 0; i < className.length; i+=1) {
if (className[i].indexOf('clr') >= 0) {
alert(className[i]);
}
}
});
http://jsfiddle.net/vJfT7/
There's no matter how you're going to order the different classes. The code will alert you a class name only of there's 'clr' as a substring in it.
Best regards.
If you don't need to find elements based on these classes (e.g. doing $('.clrtwo')) it would be nicer to store the data as a data-clr attribute. This is standards-compliant from HTML5, and is supported by jQuery using the .data() function.
In this instance, I would modify your HTML in this way:
<article class="active moreclass" data-clr="one">Article x</article>
<article class="active moreclass" data-clr="two">Article y</article>
<article class="active moreclass moreclass" data-clr="three">Article z</article>
<article class="active moreclass" data-clr="one">Article xyza</article>
I would then use Javascript like this:
$('article.active').click(function() {
console.log($(this).data('clr'));
});
jsFiddle example
If it is always the second class name which is of interest you can do this:
$("article").click(function () {
// split on the space and output the second element
// in the resulting array
console.log($(this)[0].className.split(" ")[1]);
});
http://jsfiddle.net/karim79/Z3qhW/
<script type="text/javascript">
jQuery(document).ready(function(){
$("article").click(function(){
alert($(this).attr('class').match(/\bclr[^\s]+\b/)[0]);
});
});
</script>
This should jquery script should do what you asked (tested on jsfiddle):
$(document).ready(function () {
function getClrClass(elem) {
var classes = elem.getAttribute('class').split(' ');
var i = 0;
var cssClass = '';
for (i = 0; i < classes.length; i += 1) {
if (classes[i].indexOf('clr') === 0) {
cssClass = classes[i];
i = classes.length; //exit for loop
}
}
return cssClass;
};
$('article').click(function (e) {
var cssClass = getClrClass($(this)[0]);
alert(cssClass);
e.preventDefault();
return false;
});
});
Hope this helps.
Pete
Use an attribute selector to get those that have class names that contain clr.
From there:
extract the class name (string functions)
analyze the position
determine the next element
The latter two might be best served by a translation array if you only had a few classes.
UPDATE
I agree with lonesomeday, you'd be far better off using data-* attribute to handle such logic. Using CSS as JavaScript hooks is a thing of the past.
http://jsfiddle.net/4KwWn/
$('article[class*=clr]').click(function() {
var token = $(this).attr('class'),
position = token.indexOf('clr');
token = token.substring(position, token.indexOf(' ', position));
alert(token);
});

Categories