Count psuedo elements (`::before` & `::after`) on page with VanillaJS [duplicate] - javascript

I have this simple html code. I need to be able to determine if the ::before is being applied to .icon-player-flash
<div id="TestSwitch">
<i class="icon-player-html5"></i>
<i class="icon-player-none"></i>
<i class="icon-player-flash"></i>
</div>
I thought this would work but it's always returning 0 for the length.
var flashplayer = $(".icon-player-flash:before");
console.log("count: " + flashplayer.length);
What am I missing?

Use getComputedStyle and check the value of content. If it's none then the pseudo element isn't defined:
var elem = document.querySelector(".box");
var c = window.getComputedStyle(elem,"before").getPropertyValue("content");
console.log(c);
var elem = document.querySelector(".alt");
var c = window.getComputedStyle(elem,"before").getPropertyValue("content");
console.log(c);
.box:before {
content:"I am defined"
}
<div class="box"></div>
<div class="alt"></div>
This property is used with the :before and :after pseudo-elements to generate content in a document. Values have the following meanings:
none
The pseudo-element is not generated. ref
If you want to count simply consider a filter:
const elems = document.querySelectorAll('div');
const divs = [...elems].filter(e => {
var c = window.getComputedStyle(e,"before").getPropertyValue("content");
return c != "none"
});
console.log(divs.length)
.box:before {
content:"I am defined"
}
<div class="box"></div>
<div class="box alt"></div>
<div class="alt"></div>
<div ></div>

Related

JS Queryselector for dynamically created elements with attributes

Can i use query selector to find dynamically created elements that have an attribute?
I want to find the first element with val, but this won't find them. (divs with the values a-d are there just to show this works with "normally" created elements.)
let i = 4, wrapper = document.getElementsByClassName("wrapper")[0];
while (i-- > 0) {
let div = document.createElement("div");
div.val = i;
wrapper.appendChild(div);
}
let myDiv = document.querySelector(".wrapper div[val]");
console.log("myDiv", myDiv);
<div class="wrapper">
</div>
<div class="wrapper">
<div val="a"></div>
<div val="b"></div>
<div val="c"></div>
<div val="d"></div>
</div>
Use Element.setAttribute() to set the attributes so they can be found by the querySelector:
let wrapper = document.getElementsByClassName('wrapper')[0]
let i = 4
while (i--) {
let div = document.createElement('div')
div.setAttribute('val', i)
wrapper.appendChild(div)
}
let myDiv = document.querySelector('.wrapper div[val]')
console.log('myDiv', myDiv)
<div class="wrapper"></div>

Get CSS position property of element with JavaScript

For example I have this <div> with the following CSS:
.some-div{
position: relative;
top: 50px;
}
<div class="some-div">
<p>Some content</p>
</div>
How do I get the CSS property position of the element with JavaScript (which in this example should result in the string "relative")?
Window.getComputedStyle()
The Window.getComputedStyle() method returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain
const element = document.getElementById('your_element_id');
const computedStyles = getComputedStyle(element);
const position = computedStyles.position;
Assuming your "class" has only one element:
HTML
<div class="some-div"">
<p>Some text</p>
</div>
JAVASCRIPT
let someDiv = document.getElementsByClassName('some-div')[0];
someDiv.addEventListener('click', () => {
console.log(this.getComputedStyle(someDiv).getPropertyValue('position'));
});
var element = document.querySelector('.some-div');
var style = window.getComputedStyle(element);
var pos = style.position;
console.log(pos);
Try this.
The Console output must be: "relative"

How to select the values of the value attribute?

I am trying to hide a comment with its children by a toggling function, but I want to hide them based on the value attribute.
this is my js function:
function toggle(id, lft, rgt) {
var kids = (rgt - lft - 1) / 2;
if (kids >= 1) {
var element = document.querySelectorAll("div.md#com" + id)[0].getAttribute('value');
var low = Number(element.split('-')[0]);
var high = Number(element.split('-')[1]);
for(var i = low + 1; i <= high - 1; i += 1){
var x = document.querySelectorAll("div.md#com" + i)[0]
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
}
and this is the result:
<div>
<div id="com4" class="md" value="7-10">yellow
<a onclick="toggle(4, 7, 10)" href="javascript:void(0)">[-]</a>
</div>
<div id="com5" class="md" value="8-9">not collapsing</div> //because of the id but I want to toggle them based on the value attribute
<div id="com8" class="md" value="10-11">collapses</div>
</div>
but I want:
<div>
<div id="com4" class="md" value="7-10">yellow
<a onclick="toggle(4, 7, 10)" href="javascript:void(0)">[-]</a>
</div>
<div id="com5" class="md" value="8-9">Should collapse</div>
<div id="com8" class="md" value="10-11">Should not collapse</div>
</div>
Assuming that the behavior you want is that when the <a> element is clicked on, you want to hide the elements based on the numeric range provided in its parent's value attribute. E.g. in your example, you will want to hide #com8 because it lies within the range of 7-10, but not #com5.
To achieve that, there are some changes that I would strongly recommend doing:
Give all your comments a more unique class, e.g. .com. I suspect that .md is a very generic class that you might use in other elements on the same page, so let's just get that over with.
Do not use a reserved value attribute, since that is reserved for input-like elements. Use data-* attributes instead: for example, we can use data-range="7-10". Even better, you can use data-min and data-max, so that we can avoid parsing the range. I'll leave the latter up to you.
Do not use inline JS, but rely on addEventListener to bind click events to your <a> element
Now, to the solution: if we slightly tweak your logic, it is actually very doable:
When the <a> element is clicked on, get the data-range value of its closest parent. This can be done using .closest('div.com'), and then use the dataset API to retrieve the range.
Split the data-range attribute, and remember to convert it into a number by using the unary +
Use ES6 array destructuring to assign min and max variables to the parsed data-range
Go through all .com elements:
If the element matches our trigger element's parent element, we ignore it
Otherwise, we check if its id—parsed using String.prototype.substring() to strip out the first 3 characters—lies inside or outside the numeric range
Then, use this boolean value to determine the style.display property of the element
See proof-of-concept below:
const collapseButtons = document.querySelectorAll('.collapse');
Array.from(collapseButtons).forEach(collapseButton => {
collapseButton.addEventListener('click', e => {
e.preventDefault();
// Use ES6 destructuring to assign min/max values
const [min, max] = e.target.closest('div.com').dataset.range.split('-').map(v => +v);
const comments = document.querySelectorAll('.com');
Array.from(comments).forEach(comment => {
// If the element matches the trigger element's parent, do nothing
if (comment === e.target.closest('div.com')) {
return;
}
// Otherwise, decide if we should hide or show it
const id = +comment.id.substring(3);
const shouldHide = id < min || id > max;
comment.style.display = shouldHide ? 'none' : 'block';
});
});
});
<div>
<div id="com4" class="com md" data-range="7-10">yellow
<a class="collapse" href="#">[-]</a>
</div>
<div id="com5" class="com md" data-range="8-9">Should collapse</div>
<div id="com8" class="com md" data-range="10-11">Should not collapse</div>
</div>
Try this instead
<div class="box" name="box1"></div>
<div class="box" name="box2"></div>
<div class="box" name="box3"></div>
$('.box').on('click', function(){
var value = $(this).attr('name');
alert(value);
});
Or if you want to use value attribute
$('.box').on('click', function(){
var value = $(this).attr('value');
alert(value);
});

How to check if an element is the last with this class?

So if I have a list of elements and I want to find out which one of them is the last one with this class. How can I do it without JQuery? Maybe I can get its index somehow?
in JQuery it would be something like that:
<div class="div">1</div>
<div class="div">2</div>
<div class="div">3</div>
<div class="div">4</div>
<div class="div">5</div>
<div>6</div>
jQuery(document).on('click', '.div', function() {
if ( $(this).is($(".div:last")) )
{
console.log($(this))
}
});
Link
You could either do that what you did, using the 'querySelector'
var lastElem = document.querySelector('div.class-name:last-child');
Or you could do something like this -
var elements = document.getElementsByClassName('class-name');
var lastElem = elements[elements.length - 1];
This is the standard query function, which accepts CSS selector syntax:
divs = document.querySelectorAll('.div');
last_div = divs.item(divs.length - 1);
See here.

Modern, concise, vanilla JS check whether node has a direct child matching a selector

What's the modern, concise, and fast way to test whether a node has any child that matches a given selector?
By "concise" I mean something similar to jQuery or functional-style, such as avoiding loops. I know native selectors are getting more and more of this type of thing but have not kept up with developments. If such does not yet exist across browsers then I also want to know.
I expected it to be straightforward but searching Google and SO find many false hits using jQuery or finding arbitrary descendants at any depth rather than just the immediate children. There are also some outdated questions from before many functional-style methods were added and standardized between browsers.
One option is to use the direct child combinator, >, and the :scope pseudo-class:
var children = parentElement.querySelectorAll(':scope > div');
var parentElement = document.querySelector('.container');
var children = parentElement.querySelectorAll(':scope > div');
for (var i = 0; i < children.length; i++) {
children[i].style.background = '#f00';
}
.level2 { background-color: #fff; }
<div class="container">
<span>Span</span>
<span>Span</span>
<div class="level1">Direct 'div'
<div class="level2">Nested 'div'</div>
</div>
<div class="level1">Direct 'div'
<div class="level2">Nested 'div'</div>
</div>
<div class="level1">Direct 'div'
<div class="level2">Nested 'div'</div>
</div>
</div>
Note that the :scope pseudo-class is still considered experimental and does not have full browser support. But nonetheless, it is probably the most "modern" solution (as you asked for).
Alternatively, you could use the .filter() method and check whether the parent element's children match a given selector:
function getChildren(parent, selector) {
return Array.prototype.filter.call(parent.children, function(node) {
return node.matches(selector);
});
}
Usage:
getChildren(parentElement, 'div'); // Direct children 'div' elements
function getChildren(parent, selector) {
return Array.prototype.filter.call(parent.children, function(node) {
return node.matches(selector);
});
}
var parentElement = document.querySelector('.container');
var children = getChildren(parentElement, 'div');
for (var i = 0; i < children.length; i++) {
children[i].style.background = '#f00';
}
.level2 { background-color: #fff; }
<div class="container">
<span>Span</span>
<span>Span</span>
<div class="level1">Direct 'div'
<div class="level2">Nested 'div'</div>
</div>
<div class="level1">Direct 'div'
<div class="level2">Nested 'div'</div>
</div>
<div class="level1">Direct 'div'
<div class="level2">Nested 'div'</div>
</div>
</div>
A solution with a wider browser support:
[].some.call(yourEl.children, function(e){return e.matches(".z")})
In terms of conciseness, in ES2015 (obviously, by using a transpiler) it would be even better, with arrow functions:
[].some.call(yourEl.children, e=>e.matches(".z"))
And with Array.from (ES2015):
Array.from(yourEl.children).some(e=>e.matches(".z"))
Or, in an utility function:
function childMatches(elmt, selector){
return [].some.call(elmt.children, function(e){
return e.matches(selector);
});
}
Usage
childMatches(yourElement, ".any-selector-you-want")
Use the child selector >
document.querySelectorAll('.parent-selector > .child-selector').length > 0
If you want to apply a selector starting from a specific node but can't assume :scope support, you could build a selector to a specific node like this
function selectorPath(node) {
var idx;
if (node.nodeName === 'HTML' || !node.parentNode) return node.nodeName;
idx = Array.prototype.indexOf.call(node.parentNode.children, node);
return selectorPath(node.parentNode) + ' > ' + node.nodeName + ':nth-child(' + (idx + 1) + ')';
}
Then using this in a multi-part selector might look like this
function selectChildAll(parent, selector) {
var pSelector = selectorPath(parent) + ' > ';
selector = pSelector + selector.split(/,\s*/).join(', ' + pSelector);
return parent.querySelectorAll(selector);
}
So an example of using it might be, to get all <p> and <pre> immediate children from this answer's content,
var node = document.querySelector('#answer-35028023 .post-text');
selectChildAll(node, 'p, pre'); // [<p>​…​</p>​, etc​]

Categories

Resources