I have the following case: (styling is done in SASS and unnecessary stylings are omitted.)
.header {
...
&::before {
...
position: absolute;
height: 0.5rem;
...
}
}
This creates a bar on top of the application's menu bar. In certain cases this bar has to be removed. I have read questions like these, but with no success. What would be the best way to remove this bar added by the ::before selector?
Only CSS can remove pseudo element, so you need to have an other class that display:none; the before. First declare that class in the CSS :
.header {
...
&::before {
...
position: absolute;
height: 0.5rem;
...
}
&.no-before::before{
display:none;
}
}
Then, when you want to remove it :
$('.header').addClass('no-before'); //Remove before
$('.header').removeClass('no-before'); //Re-add before
The usual way is to create a more specific rule that applies to the element(s) in question (or a later rule with the same specificity), and specify display: none to hide the pseudo in that case.
For example: Here, I want to have an X in front of <span class="foo">, but not if they're in .header:
span.foo::before {
content: 'X ';
}
.header span.foo::before {
display: none;
}
<div>
These have the X:
<span class="foo">span.foo 1</span>
<span class="foo">span.foo 2</span>
<span class="foo">span.foo 3</span>
</div>
<div class="header">
These don't:
<span class="foo">span.foo 4</span>
<span class="foo">span.foo 5</span>
<span class="foo">span.foo 6</span>
</div>
If you are manipulating the DOM by using JavaScript, you can add a class name - for instance .remove-bar - to the element having .header in order to remove the pseudo-element (generated content):
.remove-bar {
&::before { content: none; }
}
Also make sure that it is placed after the previous styles, or use a more specific selector if needed.
For remove special element use this method.
<button onclick="myFunction()">Remove</button>
<div id="myList">
<div> Coffee </div>
<div id="child2" > Tea </div>
<div> Milk </div>
</div>
your JavaScript :
<script>
function myFunction() {
const list = document.getElementById("myList");
if (list.hasChildNodes()) {
list.removeChild(list.children[0]);
}
}
</script>
you can combine above function with this code:
const parent = document.getElementById('myList');
const children = parent.children;
let index = -1;
for (let i = 0; i < children.length; i++) {
if (children[i].id === 'child3') {
index = i;
break;
}
}
alert(index); // 👉️ 2
Related
I want to add class "active" to "fav-contractors" container only when number inside "fav-con-count" span is greater than 0.
This is HTML code
<span class="fav-contractors">
<span class="fav-con-count">7</span>
</span>
and this is jQuery code
function favCounter() {
if ($(".fav-con-count").textContent > 0) {
$(".fav-contractors").addClass("active");
}
};
favCounter();
Which "if" rule should I use? I also tried something like that but it didn't work:
function favCounter() {
var favValue = $(".fav-con-count").textContent;
if (+favValue > 0)) {
$(".fav-contractors").addClass("active");
}
};
favCounter();
Node.textContent is JavaScript, not part of the jQuery library per-se. jQuery uses the .text() method to get the text by using textContent under the hood. Also, read about jQuery's toggleClass() method, you can use a second boolean parameter instead, making the if statement unnecessary.
Since you use classes it's pretty dangerous to just do $(".fav-contractors").addClass("active");, since you could:
have many .fav-contractors Elements in a single page and all will get the active class
$(".fav-con-count").text() > 0 means that only if the first of that class Element has text greater than 0 - which might also be incorrect and lead to a buggy undesired behavior.
Solution
Use .each() to iterate all your elements of a specific class
Use .closest() to traverse to a specific element ancestor (or self)
(As already mentioned) use toggleClass()
$(".fav-con-count").each(function() {
$(this).closest(".fav-contractors").toggleClass("active", $(this).text() > 0);
});
.fav-contractors { padding: 1rem; }
.active { background: gold; }
<span class="fav-contractors">
<span class="fav-con-count">7</span>
</span>
<span class="fav-contractors">
<span class="fav-con-count">0</span>
</span>
<span class="fav-contractors">
<span class="fav-con-count">3</span>
</span>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
You should use .text() instead of textContent
function favCounter() {
if ($(".fav-con-count").text() > 0) {
$(".fav-contractors").addClass("active");
}
};
favCounter();
.active {
background: yellow;
}
<span class="fav-contractors">
<span class="fav-con-count">7</span>
</span>
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
This is an example of the number 0 not adding the active class
function favCounter() {
if ($(".fav-con-count").text() > 0) {
$(".fav-contractors").addClass("active");
}
};
favCounter();
.active {
background: yellow;
}
<span class="fav-contractors">
<span class="fav-con-count">0</span>
</span>
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
Hi,
I have this markup:
<div data-users="room2">
<span data-room="room1,room2,room3">john</span>
<span data-room="room1">george</span>
<span data-room="room2">jane</span>
</div>
I want only users that have the same room data as the div parent so I wrote this css:
span { display: none; }
[data-users="room2"] [data-room*="room2"] { display: block; }
fiddle: https://jsfiddle.net/ehpt9f5z/2/
However, since the data in the div parent can change to any room number I need a variable there so the CSS selector will always match the room data from the child with its parent, something like this:
[data-users=$1] [data-room*=$1] { display: block; }
Is it even possible with pure css?
Thank you.
This is not accomplishable with CSS alone, since there is no CSS parent selector.
A JavaScript solution would be to loop through each div which has the data-users attribute, then loop through each child of the div and show the ones whose data-room attribute contains the data-users attribute value.
const divs = document.querySelectorAll('div[data-users]')
divs.forEach(e => [...e.children].forEach(f => {
if(f.dataset.room.split(',').includes(e.dataset.users)){
f.style.display = "block"
}
}))
span { display: none; }
<div data-users="room2">
<span data-room="room1,room2,room3">john</span>
<span data-room="room1">george</span>
<span data-room="room2">jane</span>
</div>
I have two divs created from PHP loop:
<div class="sku">
<span class="sku-value" data-id="150">1000</span>
<span class="sku-value" data-id="151">2000</span>
<span class="sku-value" data-id="152">3000</span>
</div>
<div class="size-values">
<span class="size-value" data-id="150">M</span>
<span class="size-value" data-id="151">L</span>
<span class="size-value" data-id="152">XL</span>
</div>
These divs has parent div called attributes.
sku-value and size-value have something in common: data-id attribute.
With CSS I'm manipulating the sku-value spans:
.sku-value {
display:none;
}
.sku-value.active {
display:inline-block;
}
With jQuery I'm displaying only the first size-value:
$('.sku-value:first').addClass('active');
So in this case when the page is loaded only this sku-value will be visible:
<span class="sku-value" data-id="150">1000</span>
With my code bellow I can successfully change the active class of each clicked size-value but how can I change the displayed sku-value that matches the data-id value?
For example: if a user click on one of size-value that has data-id 152, how can I display the sku-value that has data-id 152 and hide the current visible sku-value?
$(document).ready(function(){
$('.sku-value:first').addClass('active');
$('.attributes').find('.size-value').on('click', function(){
if ($this.hasClass('active')) {
$this.removeClass('active');
} else {
$this.closest('.size-values').find('.size-value').removeClass('active');
$this.addClass('active');
}
});
});
To achieve what you require you can retrieve the data-id of the clicked element, then use filter() to retrieve all elements by that data attribute before setting the active class on them.
Here's an example showing how to do that, and also some tweaks to the logic to make it more succinct:
$(document).ready(function() {
let $skuAttributes = $('.sku-value');
let $sizeAttributes = $('.size-value');
let $allAttributes = $skuAttributes.add($sizeAttributes);
// set default state on page load
$skuAttributes.first().addClass('active');
$sizeAttributes.first().addClass('active');
// on click of a size attribute, set active class on all relevant elements
$sizeAttributes.on('click', function() {
$sizeAttributes.removeClass('active');
let dataId = $(this).data('id');
$allAttributes.removeClass('active').filter((i, el) => el.dataset.id == dataId).addClass('active');
});
});
div { font-size: 1.3em; }
.sku-value { display: none; }
.sku-value.active { display: inline-block; }
.active { color: #C00; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="attributes">
<div class="sku">
<span class="sku-value" data-id="150">1000</span>
<span class="sku-value" data-id="151">2000</span>
<span class="sku-value" data-id="152">3000</span>
</div>
<div class="size-values">
<span class="size-value" data-id="150">M</span>
<span class="size-value" data-id="151">L</span>
<span class="size-value" data-id="152">XL</span>
</div>
</div>
Here's a functional-style, vanilla-flavored solution I came up with. Several helper functions share the heavy lifting, so the main listener function stays simple and clean.
Instead of the active class, this uses a hidden class that can apply to sku spans as well as size spans.
(I'm late to the party because I wrote this script while I was offline today.)
const
// Collects DOM elements as arrays
sizes = [...document.getElementsByClassName("size-value")],
skus = [...document.getElementsByClassName("sku-value")],
spans = [...sizes, ...skus],
// Defines helper functions
isHidden = (_this) => _this.classList.contains("hidden"),
isActive = (_this) => sizes.every((size) => isHidden(size) || size === _this),
showAll = (_these) => _these.forEach((_this) => _this.classList.remove("hidden")),
hideAll = (_these) => _these.forEach((_this) => _this.classList.add("hidden")),
filterByDataId = (_these, id) => _these.filter(_this => _this.dataset.id === id);
// Invokes listener when user clicks a size
sizes.forEach(size => size.addEventListener("click", hideAndShow));
// Defines listener
function hideAndShow({target}){ // Destructures click event
if(isActive(target)){
showAll(spans);
}
else{
hideAll(spans);
showAll(filterByDataId(spans, target.dataset.id));
}
}
div { margin: 1em 0; }
span { padding: 0.1em 0.3em; border: 1px solid grey; }
.hidden{ display: none; }
<div class="sku">
<span class="sku-value" data-id="150">1000</span>
<span class="sku-value hidden" data-id="151">2000</span>
<span class="sku-value hidden" data-id="152">3000</span>
</div>
<div class="size-values">
<span class="size-value" data-id="150">Medium</span>
<span class="size-value" data-id="151">Large</span>
<span class="size-value" data-id="152">Xtra Large</span>
</div>
I have a CSS declaration as follows:
span.boshbashbosh:nth-child(1):active:after {
content: 'FC';
}
I am trying to access the content (FC) it by using:
var content = window.getComputedStyle(document.getElementsByClassName("boshbashbosh:nth")[0], '::active').getPropertyValue('content');
alert(content);
However, all the alert does is show normal or none
Any advice on how to do this in plain JS? If I had 1000 of these, I wouldn't want to click/hover each one, is there a way I could dump some code into the developer console to do this?
There are a few issues here, the main one being that the CSS selector will only return an active element during a click interaction by the user, seeing that a click interaction causes the target element to become :active.
With that in mind, you could wrap your login in a mousedown element as shown below to extract the expected content value while the corresponding span element is :active as shown:
document.addEventListener("mousedown", () => {
/* When mouse down occours, look for the element that we want to read
pseudo content from */
var element = document.querySelector(".boshbashbosh:nth-child(1):active");
if (element) {
/* If the target element is active, read the content of the ::after
pseudo element */
var content = window.getComputedStyle(element, ":after")
.getPropertyValue("content");
alert(content);
}
})
span.boshbashbosh:nth-child(1):active:after {
content: 'FC';
}
/* Added for clarity/usability of snippet */
span {
background: pink;
margin: 1rem 0;
padding: 1rem;
display: block;
height: 1rem;
}
span.boshbashbosh:active {
background: yellow;
}
<p>Clicking first box alerts the ::after content</p>
<div>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
</div>
I've also replaced the getElementsByClassName() call with querySelector() to simplify the code. Hope that helps!
Update
To access the content of multiple pseduo elements, you could adapt the snippet above as follows:
document.querySelectorAll(".boshbashbosh").forEach((element) => {
var content = window.getComputedStyle(element, ":after")
.getPropertyValue("content");
console.log(content);
});
span.boshbashbosh:nth-child(1):after {
content: 'FC';
}
span.boshbashbosh:nth-child(2):after {
content: 'EB';
}
span.boshbashbosh:nth-child(3):after {
content: 'DA';
}
<div>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
</div>
For example, I have this menu:
function toggleHiddenContent(tabClass) {
let t = document.querySelectorAll(tabClass);
for(var i = 0; i<t.length; i++) {
t[i].classList.toggle="visible-class";
}
}
.hidden-content {
display: none;
}
.visible-class {
display: block
}
<div>
<a class="main-holder" onClick="toggleHiddenContent('.main-holder')">Main one</a>
<div class="hidden-content">Hidden content One</div>
<a class="main-holder" onClick="toggleHiddenContent('.main-holder')">Main two</a>
<div class="hidden-content">Hidden content two</div>
</div>
However, it toggles for all classes. I do understand what is the issue, but how would I match only the one that is clicked and not the ones that are not active (clicked)?
I need it in vanilla js
Thanks guys
classList.toggle is a function, not an assignable property
https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
try this
function toggleHiddenContent(tabClass) {
let t = document.querySelectorAll(tabClass);
for (var i = 0; i < t.length; i++) {
t[i].classList.toggle("visible-class");
}
}
Based on your example I would suggest you a few little changes, which will improve readability, like setting a parent <div> (makes it easy to find parent), and removing onClick from HTML. check it out
fiddle
from the answer below, I didn't know about nextElementSibling, which won't need you to change your HTML like I've suggested
You may try this:
function toggleHiddenContent(e) {
if (e.target.nextElementSibling.classList.contains("visible-class") ) {
e.target.nextElementSibling.className = "hidden-content";
} else {
e.target.nextElementSibling.className = "visible-class";
}
}
.hidden-content {
display: none;
}
.visible-class {
display: block
}
<div>
<a class="main-holder" onClick="toggleHiddenContent(event)">Main one</a>
<div class="hidden-content">Hidden content One</div>
<a class="main-holder" onClick="toggleHiddenContent(event)">Main two</a>
<div class="hidden-content">Hidden content two</div>
</div>
You could try something like this (and also prevent obtrusive javascript):
//Get the element(s) you want to interact with and store these in an array:
let myElements = document.getElementsByClassName("main-holder");
//Loop through the array to add an event listener to every interactable element:
for (let i = 0; i < 10; i++) {
if (myElements[i]) {
myElements[i].addEventListener("click", function() {
//Your function:
toggleHiddenContent("visible-class", i);
});
}
}
function toggleHiddenContent(tabClass, target) {
//Get the element(s) you want to manipulate (or adjust) and store these in an array:
let targetElements = document.getElementsByClassName("hidden-content");
//Use the index you got from clicking on the previous element to determine which targeted element to manipulate (or adjust):
targetElements[target].classList.toggle(tabClass);
}
.hidden-content {
display: none;
}
.visible-class {
display: block;
}
<div>
<a class="main-holder">Main one</a>
<div class="hidden-content">Hidden content One</div>
<a class="main-holder">Main two</a>
<div class="hidden-content">Hidden content two</div>
</div>
JSFiddle
If you need any more information, please let me know in a comment.