click all buttons on page except when parent has a specific class - javascript

I want to click all buttons on a page that have the attribute data-capture='noiseClicked'
This is my code so far:
javascript: (function() {
var followButtons = $("li.js-profile-card button[data-capture='noiseClicked']");
var index = followButtons.length - 1;
follow();
function follow() {
if (index >= 0) {
$(followButtons[index--]).click();
setTimeout(follow, 1);
}
}
})();
However I want to exclude buttons that have a parent of li.noise--active or li.friend--active
So the following would be clicked:
<li class="js-profile-card noise--active"><button data-capture="noiseClicked" type="button"></button></li>
but the following would not be clicked...
<li class="js-profile-card noise--active"><button data-capture="noiseClicked" type="button"></button></li>
or
<li class="js-profile-card friend--active"><button data-capture="noiseClicked" type="button"></button></li>
I thought that jquery's not selector would be helpful here, but I'm not sure how to use it to exclude a parent element with a specific attribute and I don't know how to exclude two different attributes (noise--active and friend--active)
Thanks.

You can use parent & hasClass methods for this:
var indexToSet = index--;
if( !$(followButtons[indexToSet]).parent().hasClass( 'noise--active' ) && !$(followButtons[indexToSet]).parent().hasClass( 'friend--active' )) {
$(followButtons[indexToSet]).click();
}
EDIT:
to travel up in the node list better to use closest() method:
var indexToSet = index--;
if( !$(followButtons[indexToSet]).closest( 'noise--active' ).length && !$(followButtons[indexToSet]).closest( 'friend--active' ).length ) {
$(followButtons[indexToSet]).click();
}

:not selector might come handy:
var followButtons = $("li.js-profile-card:not(.noise--active,.friend--active) button[data-capture='noiseClicked']");

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)
})

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);
});

How to remove div elements with Javascript?

Lets say I have a webpage, and all I'm interested is the div with id "content", i.e:
<div id="content"></div>
How do I remove all the other div elements, and just display the div I want?
var all_div_nodes = document.querySelectorAll('div'),
len = all_div_nodes.length,
current = null;
while( len-- ) {
current = all_div_nodes[len];
if( current.parentNode ) {
if( current .id !== 'content' )
current .parentNode.removeChild( current );
}
}
If you can afford using a library like jQuery, this would be even more trivial:
$('div').not('#content').remove();
If you want to remove the sibling DIVs, using jQuery, you can write:
$("#content").siblings("div").remove();

Javascript change class of an element

I have ul list and I need to change the class of one of <li> tags with javascript:
<ul>
<li>...</li>
<li class="something"> <- need to change this class to "myclass" (javascript goes here)</li>
<li>..</li>
</ul>
Thank you.
using jQuery (naturally):
$(function(){
$("li.something").removeClass("something").addClass("myclass");
});
As there seems to be alot of jquery answers and it's not always possible to use jquery (for example if your customer/company won't let you use it arrgh!), here is a plain javascript example.
// Where 'e' is the element in question, I'd advise using document.getElementById
// Unless this isn't possible.
// to remove
if ( e.className.match(/something/) ) {
e.className = e.className.replace("something", "")
}
// to add back in
if ( !e.className.match(/something/) ) {
e.className += " something"
}
This will work with multiple classes, for example:
<li class="something another">...</li>
Using regular javascript:
var listitems = document.getElementsByTagName("li");
for (int i = 0; i < listitems.length; i++)
{
if (listitems[i].className == "something")
{
listitems[i].className = "new class name";
break;
}
}
If your <li> tag had an id attribute, it would be easier, you could just do
document.getElementById("liID").className = "newclassname";
Using JQuery:
$('ul li:nth-child(2)').attr('class', 'myclass');

Is there a way to select sibling nodes?

For some performance reasons, I am trying to find a way to select only sibling nodes of the selected node.
For example,
<div id="outer">
<div id="inner1"></div>
<div id="inner2"></div>
<div id="inner3"></div>
<div id="inner4"></div>
</div>
If I selected inner1 node, is there a way for me to access its siblings, inner2-4 nodes?
Well... sure... just access the parent and then the children.
node.parentNode.childNodes[]
or... using jQuery:
$('#innerId').siblings()
Edit: Cletus as always is inspiring. I dug further. This is how jQuery gets siblings essentially:
function getChildren(n, skipMe){
var r = [];
for ( ; n; n = n.nextSibling )
if ( n.nodeType == 1 && n != skipMe)
r.push( n );
return r;
};
function getSiblings(n) {
return getChildren(n.parentNode.firstChild, n);
}
var sibling = node.nextSibling;
This will return the sibling immediately after it, or null no more siblings are available. Likewise, you can use previousSibling.
[Edit] On second thought, this will not give the next div tag, but the whitespace after the node. Better seems to be
var sibling = node.nextElementSibling;
There also exists a previousElementSibling.
Quick:
var siblings = n => [...n.parentElement.children].filter(c=>c!=n)
https://codepen.io/anon/pen/LLoyrP?editors=1011
Get the parent's children as an array, filter out this element.
Edit:
And to filter out text nodes (Thanks pmrotule):
var siblings = n => [...n.parentElement.children].filter(c=>c.nodeType == 1 && c!=n)
From 2017:
straightforward answer: element.nextElementSibling for get the right element sibling. also you have element.previousElementSibling for previous one
from here is pretty simple to got all next sibiling
var n = element, ret = [];
while (n = n.nextElementSibling){
ret.push(n)
}
return ret;
have you checked the "Sibling" method in jQuery?
sibling: function( n, elem ) {
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
return r;
}
the n.nodeType == 1 check if the element is a html node and n!== exclude the current element.
I think you can use the same function, all that code seems to be vanilla javascript.
There are a few ways to do it.
Either one of the following should do the trick.
// METHOD A (ARRAY.FILTER, STRING.INDEXOF)
var siblings = function(node, children) {
siblingList = children.filter(function(val) {
return [node].indexOf(val) != -1;
});
return siblingList;
}
// METHOD B (FOR LOOP, IF STATEMENT, ARRAY.PUSH)
var siblings = function(node, children) {
var siblingList = [];
for (var n = children.length - 1; n >= 0; n--) {
if (children[n] != node) {
siblingList.push(children[n]);
}
}
return siblingList;
}
// METHOD C (STRING.INDEXOF, ARRAY.SPLICE)
var siblings = function(node, children) {
siblingList = children;
index = siblingList.indexOf(node);
if(index != -1) {
siblingList.splice(index, 1);
}
return siblingList;
}
FYI: The jQuery code-base is a great resource for observing Grade A Javascript.
Here is an excellent tool that reveals the jQuery code-base in a very streamlined way.
http://james.padolsey.com/jquery/
The following function will return an array containing all the siblings of the given element.
const getSiblings = node => [...node.parentNode.children].filter(c => c !== node)
// get "c" element siblings (excluding itself)
const siblingsToC = getSiblings(document.querySelector('.c'))
console.log( siblingsToC )
<ul>
<li class='a'>a</li>
<li class='b'>b</li>
<li class='c'>c</li>
<li class='d'>d</li>
<li class='e'>e</li>
</ul>
Just pass the selected element into the getSiblings() function as it's only parameter.
Here's how you could get previous, next and all siblings (both sides):
function prevSiblings(target) {
var siblings = [], n = target;
while(n = n.previousElementSibling) siblings.push(n);
return siblings;
}
function nextSiblings(target) {
var siblings = [], n = target;
while(n = n.nextElementSibling) siblings.push(n);
return siblings;
}
function siblings(target) {
var prev = prevSiblings(target) || [],
next = nexSiblings(target) || [];
return prev.concat(next);
}
Use document.querySelectorAll() and Loops and iteration
function sibblingOf(children,targetChild){
var children = document.querySelectorAll(children);
for(var i=0; i< children.length; i++){
children[i].addEventListener("click", function(){
for(var y=0; y<children.length;y++){children[y].classList.remove("target")}
this.classList.add("target")
}, false)
}
}
sibblingOf("#outer >div","#inner2");
#outer >div:not(.target){color:red}
<div id="outer">
<div id="inner1">Div 1 </div>
<div id="inner2">Div 2 </div>
<div id="inner3">Div 3 </div>
<div id="inner4">Div 4 </div>
</div>
jQuery
$el.siblings();
Native - latest, Edge13+
[...el.parentNode.children].filter((child) =>
child !== el
);
Native (alternative) - latest, Edge13+
Array.from(el.parentNode.children).filter((child) =>
child !== el
);
Native - IE10+
Array.prototype.filter.call(el.parentNode.children, (child) =>
child !== el
);
var childNodeArray = document.getElementById('somethingOtherThanid').childNodes;
1) Add selected class to target element 2) Find all children of parent element excluding target element 3) Remove class from target element
<div id = "outer">
<div class="item" id="inner1">Div 1 </div>
<div class="item" id="inner2">Div 2 </div>
<div class="item" id="inner3">Div 3 </div>
<div class="item" id="inner4">Div 4 </div>
</div>
function getSiblings(target) {
target.classList.add('selected');
let siblings = document.querySelecttorAll('#outer .item:not(.currentlySelected)')
target.classList.remove('selected');
return siblings
}
You can access the following sibling nodes, with the currentNode.nextSibiling property.
This is how you can do in the event delegation way, which is a dynamic way to add event listeners
document.addEventListener('click', (event) => {
if (event.target.matches("#inner1")) {
console.log(event.targert.nextSibling); //inner2 div
console.log(event.targert.nextSibling.nextSibling); //inner3 div
/* The more of the property you keep appending the further it goes to
the next sibling */
}
})
My use case was different. I had to select a few spans which didn't have any id/classes (nor their parents), just an entry point (#target). Once you have that, run a querySelectorAll on its parent with the appropriate selector, using :scope as you can't simply use > div or > span or > .foo.
Note that this approach ALSO selects the target element, if it matches the selector. In the below example, I'd have to use :scope > span:not(#target) to avoid selecting the entry point.
const spanSiblings = document.getElementById("target").parentNode.querySelectorAll(":scope > span");
console.log([...spanSiblings].map(e => e.innerText));
<div>
<span>One</span>
<span id="target">Target</span>
<div>A</div>
<span>Two</span>
<div>B</div>
<div>Hey</div>
</div>
BEST SOLUTION:
This is the best solution according my opinion:
let inner2 = event.target.parentNode.querySelector(`#inner2`)
/*Or if you have already stored the inner1 node to a variable called: inner1*/
let inner2 = inner1.parentNode.querySelector(`#inner2`)
At the first line the event.target will be the inner1 node, if we click on that. The parentNode will be the "outer" node, and on the partent node we start a search ( .querySelector(#inner2) ) to select the inner2 node.
OTHER SOLUTIONS:
I list other possible options, but they are not that flexible, since at them the sequence of the nodes are matter, which makes the code fragile, if we later add another node to the parent the whole code will break, what we want to avoid:
2)
This selects the first child (this index starts from 1, and NOT from 0)
node.parentNode.childNodes[1]
3) Assume that you have already selected inner1Node to a variable, the next sibling you can get:
let inner2Node = inner1Node.nextElementSibling;
4) The previous sibling you can get:
let inner1NodeAGAIN = inner2Node.previousElementSibling;
x1 = document.getElementById('outer')[0]
.getElementsByTagName('ul')[1]
.getElementsByTagName('li')[2];
x1.setAttribute("id", "buyOnlineLocationFix");

Categories