I am trying to verify if the clicked cell on a devexpress grid is valid according to the following rules:
Click on a cell that has classes: .dxgv.dx-ar (This part is working)
If that element contains some element of the class gridValue AND this (child) element has a father with name: 'allowsEdit' (THIS CODE IS NOT WORKIING)
I was trying to avoid using the ".parent()..." adicional line, but combine multiple JQuery selector rules, but till now I was not able.
Any ideas?
JS
$('#' + gridIdName).unbind('click').on('click', '.dxgv.dx-ar', function (e) {
if ($(this).find('.gridValue:parent[name="allowsEdit"]').length !== 0) {
console.log('FOUND');
return
}
console.log('NOT FOUND');
});
HTML
/******** VALID *************/
<td id="devGrid1_tccell2_13" class="dxgv dx-ar">
<div style="width: 100%;" name="allowsEdit" class="contextMenuElement TargetIdstate VarietyId76 Year2018 CountryId2 PlanId11437">
<div style="width:100%;" id="state_76_2_2018" class="gridValue gridValueMin1 gridValueMax11">
2000
</div>
<div style="clear: both;"></div>
</div>
</td>
/******** NOT VALID *************/
<td id="devGrid1_tccell3_7" class="dxgv dx-ar" style="background-color:#E4AA8A;">
<div style="width: 100%;" name="" class="contextMenuElement TargetIdactual VarietyId82 Year2018 CountryId1 PlanId12732">
<div style="width:37px;" id="actual_82_1_2018" class="gridValue">
40000
</div>
<div style="clear: both;"></div>
</div>
</td>
You can use a simple css selector for this
[name="allowsEdit"] > .gridValue
this selects the .gridValue element. if you directly want the allowsEditElement you can use:
[name="allowsEdit"]:has(.gridValue)
find .gridValue class elements where direct parent have class: .allowEdit
$(this).find('[name="allowsEdit"]:has(.gridValue)').length
For more information you can read Mozzila's documentation about this
Click on a cell that has classes: .dxgv.dx-ar
If that element contains some element of the class gridValue AND this element has a father with name: 'allowsEdit'
Your HTML doesn't match that description, but the above is a straightforward combination of:
An attribute value selector
A descendant combinator
A pair of class selectors
A :has selector (which is a jQuery addition)
So all in one it would be: [name=allowsEdit] .dxgv.dx-ar:has(.gridValue)
E.g.:
$('#' + gridIdName).unbind('click').on('click', '[name=allowsEdit] .dxgv.dx-ar:has(.gridValue)', function (e) {
That's if you don't want the clicks at all unless it's a match.
But if you want the click regardless and then just want to check the other things afterward:
$('#' + gridIdName).unbind('click').on('click', '.dxgv.dx-ar', function (e) {
var $this = $(this);
if ($this.closest("[name=allowEdit]").length && $this.find(".gridValue").length) {
// Match
} else {
// No match
}
Related
<div>
<br>
<br>
<br>
</div>
I have an HTML page that has the above div element in it. How can I scan the page an look for a DIV that contains specific items? In this example I know I have three <br> in a row and that's it. The DIV element does not have a class or an ID, and I would like to delete it.
You can target elements in a row by using the sibling selector, +. You can make sure the target elements are a direct child of the div by using the > selector.
You can find the div that contains the three br elements like so:
document.querySelector('div > br + br + br').parentNode;
To remove this element from the DOM use the remove method.
const elToDelete = document.querySelector('div > br + br + br')?.parentNode;
elToDelete?.remove();
Edit:
Added optional chaining syntax to the answer to show how to prevent undefined errors from being thrown.
You can use the :has pseudo-class to select the div directly
var elem = document.querySelectorAll("div:has(>br+br+br)");
console.log([...elem])
elem.forEach(x=>x.remove())
<div id=a> a
<br/>
<br/>
<br/>
</div>
<div id=b> b
<br/>
<br/>
</div>
<div id=c> c
<br/>
<br/>
<not-br/>
<br/>
</div>
You can try this:
[].slice.call(document.getElementsByTagName("div")).forEach(function(div){
var count = 0;
if(div.children.length === 3)
[].slice.call(div.children).forEach(function(br, i){
if(br.nodeName === "BR")
count++;
});
if(count > 2)
div.parentElement.removeChild(div);
});
You can just hide it with CSS
div:has(>br+br+br) {
display: none;
}
<div>1</div>
<div><br/><br/><br/></div>
<div>3</div>
Best solution that will work cross-browser is to tackle this programmatically. Note that :has() pseudo class doesn't work on Firefox, IE or Opera (see https://caniuse.com/css-has).
Here's a concise functional Javascript approach:
[].filter.call(document.querySelectorAll('div'), el => el.querySelectorAll('br').length === 3)
I'm attempting to track events for all UI elements on a page. The page contains dynamically generated content and various frameworks / libraries. Initially I tracked elements through creating a css class "track" , then adding style "track" to tracked elements. elements are then tracked using :
$('.track').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
As content can be dynamically generated I wanted a method to track these elements also. So tried this using wildcard jQuery operator.
In this fiddle : https://jsfiddle.net/xx68trhg/37/ I'm attempting to track all elements using the jquery '*' selector.
Using jQuery '*' selector appears to fire the event for all elements of given type.
So for this case if is clicked all the click event is fired for all divs. But id is just available for div being clicked.
For the th element the click event is fired twice , what is reason for this ?
Can the source be modified that event is fired for just currently selected event ?
fiddle src :
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<th id="th">tester</th>
You can try with:
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<tr>
<td>Cols 1</td>
<td id="td">Cols 2</td>
</tr>
</table>
<p id="th">tester</p>
You may want to use event delegation to target the elements you need. Advantage is that this also works for dynamically generated elements. See code for an example of this.
// method to add/set data-attribute and value
const nClicksInit = (element, n = "0") => element.setAttribute("data-nclicked", n);
// add data-attribute to all current divs (see css for usage)
// btw: we can't use the method directly (forEach(nClicksInit))
// because that would send the forEach iterator as the value of parameter n
document.querySelectorAll("div").forEach(elem => nClicksInit(elem));
// add a click handler to the document body. You only need one handler method
// (clickHandling) to handle all click events
document.querySelector('body').addEventListener('click', clickHandling);
function clickHandling(evt) {
// evt.target is the element the event is generated
// from. Now, let's detect what was clicked. If none of the
// conditions hereafter are met, this method does nothing.
const from = evt.target;
if (/^div$/i.test(from.nodeName)) {
// aha, it's a div, let's increment the number of detected
// clicks in data-attribute
nClicksInit(from, +from.getAttribute("data-nclicked") + 1);
}
if (from.id === "addDiv") {
// allright, it's button#addDiv, so add a div element
let newElement = document.createElement("div");
newElement.innerHTML = "My clicks are also tracked ;)";
const otherDivs = document.querySelectorAll("div");
otherDivs[otherDivs.length-1].after(newElement);
nClicksInit(newElement);
}
}
body {
font: 12px/15px normal verdana, arial;
margin: 2em;
}
div {
cursor:pointer;
}
div:hover {
color: red;
}
div:hover:before {
content: '['attr(data-nclicked)' click(s) detected] ';
color: green;
}
#addDiv:hover:after {
content: " and see what happens";
}
<div id="1">
Click me and see if clicks are tracked
</div>
<div id="2">
Click me and see if clicks are tracked
</div>
<div id="3">
Click me and see if clicks are tracked
</div>
<p>
<button id="addDiv">Add a div</button>
</p>
<h3 id="th">No events are tracked here, so clicking doesn't do anything</h3>
You can invoke the stopPropagation and the condition this === e.currentTarget to ensure invoke the handler function of the event source DOM.
And you must know the <th> tag must wrapped by <table>, otherwise it will not be rendered.
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function(e) {
if (this === e.currentTarget) {
e.stopPropagation();
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<th id="th">tester</th>
</table>
I'm attempting to select the .item div which contains both tagOne and tagTwo span elements.
My div structure is as follows:
<div id="items">
<div id="block1" class="item">
<span class="tagOne tag">One</span>
<span class="tagTwo tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tagOne tag">Java</span>
</div>
</div>
Using the following jQuery I'm able to locate the tags (with their parent div's) separately.
var blocks = $('#items .item');
blocks.filter('.item').find('[class*="tagOne"]').parent();
blocks.filter('.item').find('[class*="tagTwo"]').parent();
However, once I try to combine them to narrow it down to the one div that contains them both, I get no results and I can't seem to work out why!
blocks.filter('.item').find('[class*="tagOne"][class*="tagTwo"]');
My understanding is that the comma syntax will create an OR expression, and without creates an AND expression. I'm after the AND expression as I only want to return the div that contains all the criteria, or nothing at all.
Note: I'm doing it this way because I'm creating a toggle-filter based on the tags, and the criteria (i.e. tagOne, tagTwo) is a concatenation of the tags selected by the user (not shown) so it is preferable to try to do it in one operation.
EDIT: Moved duplicate id's to class names instead to make it valid and tweaked JavaScript code accordingly.
First of all, the ID should be unique. Now, the markup contains two elements with ID tagOne which is invalid markup.
You can use class instead of ID.
Select any of the element from the two(.tagOne or .tagTwo in this case)
Use siblings() to select the sibling element having the other class
Use closest() to select closest ancestor matching the selector.
The step #1, #2 and #3 above will select only those .item elements having both .tagOne and .tagTwo as descendent.
Code:
$('.tagOne') // Select one of the element
.siblings('.tagTwo') // Get second element if it is sibling
.closest('.item') // Get the closest ancestor
$('.tagOne') // Select one of the element
.siblings('.tagTwo') // Get second element if it is sibling
.closest('.item') // Get the closest ancestor
.addClass('selected'); // For Demo purpose
.item {
color: red;
}
div.selected {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
<div id="block1" class="item">
<span class="tagOne tag">One</span>
<span class="tagTwo tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tagOne tag">Java</span>
</div>
<div id="block3" class="item">
<span class="tagTwo tag">I Love JavaScript</span>
</div>
</div>
You can also use filter as follow.
Iterate over all the .item elements using filter()
Use context selector to check if the current .item has descendent .tagOne and .tagTwo.
Use length property on the jQuery object to get the number of elements selected by the selector.
Code:
$('.item').filter(function() {
return $('.tagOne', this).length && $('.tagTwo', this).length;
})
// Fiddle: https://jsfiddle.net/tusharj/8tuu1wxs/1/
// Iterate over all elements having item class
$('.item').filter(function() {
return $('.tagOne', this).length && $('.tagTwo', this).length;
}).addClass('selected');
.item {
color: red;
}
.selected {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
<div id="block1" class="item">
<span class="tagOne tag">One</span>
<span class="tagTwo tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tagOne tag">Java</span>
</div>
<div id="block3" class="item">
<span class="tagTwo tag">I Love JavaScript</span>
</div>
</div>
If the sequence/order of the elements is fixed, CSS general sibling selector ~ or adjacent sibling selector + can be used.
$('.tag1 ~ .tag2').closest('.item')
OR
$('.tag1 + .tag2').closest('.item')
// Fiddle: https://jsfiddle.net/tusharj/amdoLfou/1/
$('.tag1 ~ .tag2') // + can also be used instead of ~
.closest('.item') // Get closest ancestor
.css('color', 'blue'); // For Demo purpose
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
<div id="block1" class="item">
<span class="tag1 tag">One</span>
<span class="tag2 tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tag1 tag">Java</span>
</div>
</div>
While you've already accepted an answer, I felt that this is a question worthy of a plain JavaScript, rather than simply a jQuery, solution. So, with that in mind, I'd like to offer the following approach (which does use some ECMAScript 6 feastures, so does require a fairly modern browser):
// using an Immediately-Invoked Function Expression syntax,
// so that the enclosed function will be executed when
// encountered, rather than requiring the user to call it
// explicitly (this would need to run in a DOMReady callback
// or once the DOM has been constructed, however):
(function hasAll(opts) {
// setting the default settings for the function:
var settings = {
// a CSS Selector string to identify the ancestor
// element that you wish to identify:
'ancestorSelector': 'div',
// an array of CSS Selectors to identify the
// descendants by which the ancestor should
// be found:
'descendantSelectors': []
}
// looking at the named (not inherited) properties
// of the opts Object supplied by the user:
for (var property in opts) {
// if the opts Object has a given property
// name we set the corresponding property
// of the settings Object to be equal to that
// property-value:
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
// finding all the elements represented by the first selector
// of the user-supplied selectors contained within an element
// matching the ancestor selector:
var firstElements = document.querySelectorAll(
settings.ancestorSelector + ' ' + settings.descendantSelectors[0]
),
// converting the NodeList returned by document.querySelectorAll()
// into an Array, using Array.from:
arrayOfFirsts = Array.from(firstElements),
// here we iterate over that Array, using Array.prototype.filter():
hasSiblings = arrayOfFirsts.filter(function(n) {
// we look for the parentNode of the current node (n):
var p = n.parentNode;
// we use Array.prototype.every() to ensure that every
// selector in the descendantSelectors Array returns
// a Node (document.querySelector() returns only the
// first node matching the given selector, or null if
// there is no element matching that selector).
// if Array.prototype.every() returns true (all elements
// of the Array match the supplied test) then the current
// node (n) is retained in the array returned by
// Array.prototype.filter():
return settings.descendantSelectors.every(function(selector) {
// Array.prototype.every() returns a Boolean,
// true : if all elements of the Array match
// the supplied test/assessment,
// false: if *any* of the elements of the Array
// fail to match.
// this is the test that we're matching against:
return p.querySelector(selector) !== null;
});
});
// here we iterate over the hasSiblings Array, and use
// Array.prototype.map() to form a new Array, using
// an Arrow function to take the current node (n)
// and find, and return, the closest element to that
// node which matches the supplied settings.ancestorSelector:
var found = hasSiblings.map(n => n.closest(settings.ancestorSelector));
// returning that array to the calling context:
return found;
})({
// this is the 'opts' Object that we're passing to the
// IIFE-contained function:
'ancestorSelector': '.item',
'descendantSelectors': ['.tagOne', '[data-demo]']
// using Array.prototype.forEach() to iterate over the
// returned elements, to add the class 'hasAll' to the
// the classList (the list of class-names) of the given
// node (n):
}).forEach(n => n.classList.add('hasAll'));
(function hasAll(opts) {
var settings = {
'ancestorSelector': 'div',
'descendantSelectors': []
}
for (var property in opts) {
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
var firstElements = document.querySelectorAll(
settings.ancestorSelector + ' ' + settings.descendantSelectors[0]
),
arrayOfFirsts = Array.from(firstElements),
hasSiblings = arrayOfFirsts.filter(function(n) {
var p = n.parentNode;
return settings.descendantSelectors.every(function(selector) {
return p.querySelector(selector) !== null;
});
});
var found = Array.from( hasSiblings.map(n => n.closest(settings.ancestorSelector)) );
return found;
})({
'ancestorSelector': '.item',
'descendantSelectors': ['.tagOne ~ .tagTwo']
}).forEach(n => n.classList.add('hasAll'));
div {
width: 50%;
margin: 0.5em auto;
border: 2px solid #000;
border-radius: 1em;
padding: 0.5em;
box-sizing: border-box;
}
.hasAll {
border-color: #f90;
}
.hasAll span {
color: #f90;
font-weight: bold;
}
<div id="items">
<div id="block1" class="item">
<span class="tag tagOne">One</span>
<span class="tag tagTwo">Two</span>
</div>
<div id="block2" class="item">
<span class="tag tagOne">Java</span>
</div>
<div id="block3" class="item">
<span class="tag tagOne" data-demo="false">tag-one</span>
<span class="tag tagTwo">tag-two</span>
<span class="tag" data-demo="true">tag-three</span>
</div>
</div>
JS Fiddle demo.
Note that, with the above function, an ancestor element will be matched if any of its descendants', or its descendants' siblings, matches multiple selectors.
References:
Array.from().
Array.prototype.every().
Array.prototype.filter().
Array.prototype.forEach().
Array.prototype.map().
Arrow functions.
document.querySelector().
document.querySelectorAll().
Element.classList.
Element.closest().
for...in statement.
Object.hasOwnProperty().
Node.parentNode.
Try this
blocks.filter('.item').find('[id="tagOne"],[id="tagTwo"]');
Try using has jquery has selector it searches if selected nodes have certain children:
https://jsfiddle.net/96gbf7xg/
So I'm using classes to sort different content, but I'm not actually sure how to apply this sorting.
<div class="class1"><div class="heads">Title</div>
<div class="description"><p>Class 1 Item 1</p></div></div>
<div class="class2"><div class="heads">Title</div>
<div class="description"><p>Class 2 Item 1</p></div></div>
<div class="class2"><div class="heads">Title</div>
<div class="description"><p>Class 2 Item 2</p></div></div>
<div class="class3"><div class="heads">Title</div>
<div class="description"><p>Class 3 Item 1</p></div></div>
So let's say the user clicks a button that says 'Class 2'. I want the opacity of everything that is not class 2 to be, say, .5 while class 2's opacity stays at 1. I've tried using .not(), but I'm not familiar with it and most examples use it in conjunction with .siblings(), and I don't want the siblings to fade either. Help? I'm not sure what to do.
Edit: Sorry about the orphan s. ^_^; Fixed them!
http://jsfiddle.net/orjj65g0/7/
$("#container button").click(function() {
var className = $(this)[0].className;
$("#container button").each(function() {
if($(this)[0].className !== className) {
$(this).next().addClass("op05");
$(this).next().removeClass("op1");
} else {
$(this).next().addClass("op15");
$(this).next().removeClass("op05");
}
});
});
With $("#container button").click(...) you access every button in #container.
$(this).[0].className is the class name of the button you have clicked.
After you have clicked the button, you go through every button in the container:
$("#container button").each(...)
In the container you compare the class names with the clicked class name. If there are not the same, than add the class "op05" to the div after the button and remove the class "op1" from the div after the button:
(Example:
<button class="classN">click</button>
<div class="content">div after button</div>
$(".classN").next()...
)
Here:
$(this).next()...
And with all the div's after the button(s), that have the same class name happens the same with the 'opposite' class names.
$("div").not(".class2").css("opacity", "0.5")
will set opacity of all divs except for ones with class class2 to 0.5.
If you are using a container:
$('.container>div:not(.class2)').css('opacity', 0.5);
1. You have invalid HTML. a tag opening is missing. Based on my assumptions, that's how it should look like:
<div class="classX">
Title
</div>
<div class="description">
<p>Class X Item 1</p>
</div>
But it's very unintuitive syntax. What is .description content for? I suggest you to rewrite syntax. For example:
<div class="classX">
Title
<div class="description">
<p>Class X Item 1</p>
</div>
</div>
2. You can use .not() method or :not() selector in jQuery
According to my version of HTML. Let's code!
$("a").on('click', function(){
var $t = $(this).parent(); // clicked div.class2 for example
$t.css("opacity", 1).siblings().css("opacity", 1); // undo selection
$t.siblings().not("."+$t.attr("class")).css("opacity", 0.5);
// hide other classes. Equivalent with selector:
//$t.siblings(":not(."+$t.attr("class")+")").css("opacity", 0.5);
});
Check it that's what you want: http://jsfiddle.net/Tymek/2k85m8r9/
Here is my HTML code:
<th>
Click<br/>
<img class="magnifier" height="66" src="../Images/magnifier-zoom.png" width="75"><br/>
To Enlarge
</th>
I have a jQuery script that when clicked it toggles an enlarge class, so when someone clicks to enlarge I want to change the enlarge word to shrink would there be any simple way of doing this in jQuery?
Or do you guys think I am better off having 2 <div>'s or even <span> elements and toggle the display of each element?
There are numerous ways to do this. You could leverage pseudo-element content, do string manipulation with JavaScript, and more. In the end, the best approach is to probably just toggle the visibility of a couple nested elements:
I've placed a default "shrink" class on my td element. Within, I have a couple span elements customized with explicit data-do attributes indicating the purpose of each:
<td class="shrink">
Click to
<span data-do="enlarge">Enlarge</span>
<span data-do="shrink">Shrink</span>
<img src="..." />
</td>
We target the data-do attributes that are nested within elements that have corresponding classes, and we disable the display of these elements:
.shrink [data-do='shrink'],
.enlarge [data-do='enlarge'] {
display: none;
}
In order to toggle the class of the td element, we bind up some simple jQuery:
$("td").on("click", function () {
$(this).toggleClass("shrink enlarge");
});
Anytime a td is clicked (you can make the selector specific to a single td), we add toggle the "shrink" and "enlarge" classes. If "enlarge" was present to begin with, it is removed; otherwise it will be added. The same goes for "shrink".
Change your HTML to
<th>
<div>Click</div>
<img class="magnifier" height="66" src="../Images/magnifier-zoom.png" width="75">
<div>To Enlarge</div>
</th>
To have elements instead of text nodes.
Then you can do simple:
$('.magnifier').click(function() {
var $next = $(this).next();
$next.text($next.text() == 'To Enlarge' ? 'To Shrink' : 'To Enlarge');
})
Demo: http://jsfiddle.net/B7GDQ/
This is the direct answer to your questions, although not the best method:
$(".magnifier").on("click", function () {
if ($(this).hasClass('enlarge')) {
$(this).removeClass('enlarge');
this.parentNode.lastChild.textContent = 'To Enlarge'
}else{
$(this).addClass('enlarge');
this.parentNode.lastChild.textContent = 'To Shrink';
}
});
The ideal method would be to use pseudo elements:
Codepen
HTML:
<div class="magnifier">
Click<br/>
<img height="66" src="../Images/magnifier-zoom.png" width="75"><br/>
</div>
CSS:
.magnifier::after {
content: 'To Enlarge';
}
.magnifier.enlarge::after {
content: 'To Shrink';
}
JS:
$(".magnifier").on("click", function () {
$(this).toggleClass('enlarge');
});