How to remove active class from other sections - javascript

const addActiveClass = () => {
if( isInViewport(section) ) {
section.classList.add('active-section');
// How to remove the 'active-section' from other sections
}
}
}
what is the code should be written to remove active class from other section?
In jQuery this may be easy but what about pure js?

Well, you question is a bit confusing because there is no context.
The simplest approach would be to have an array or a node object with all your sections.
Then loop through theses sections with a for loop to remove the active class from the one you want ?
For example here I have 3 sections.
I want every section to have the section--active class when I click on it.
But I also want that only one at the time can have the section--active class :
<div class="section section--1 section--active"></div>
<div class="section section--2"></div>
<div class="section section--3"></div>
In javascript I will get them in a node object (kind of array) :
const sections = document.querySelectorAll('.section')
Then I can bind click event to each section :
sections.forEach(section => {
section.addEventListener('click', () => {
// I loop again to remove the 'section--active' from all others sections
// To avoid confusion, I use 'el' as a name for each section in sections
sections.forEach(el => el.classList.remove('section--active'))
// Then I add the 'section--active' to the clicked element
section.classList.add('section--active')
})
})

You'll need to first loop over all the elements that have the active-section class with document.querySelectorAll(".active-section") and use classList.remove(). Then, once all elements have had that class removed, add it back to just the current element in question.
Something along these lines:
const addActiveClass = () => {
if( isInViewport(section) ) {
// Loop over all the elements that are active
document.querySelectorAll(".active-section").forEach(function(item){
item.classList.remove('active-section'); // Remove the class
});
// Then add it to the current element
section.classList.add('active-section');
}
}

Related

Why can't I remove this class with javascript?

I'm trying to make a skeleton loading screen by having a class 'skeleton' on the elements which styles them then removing it with javascript after a timeout. The issue is that i can't get the javascript to work.
Here's my javascript to remove the class, why isn't it working?
const timeout = setTimeout(loading, 3000);
function loading() {
const element = document.getElementById("skeleton");
element.classList.remove("skeleton");
}
What I think is happening is that you have too many "skeleton" elements with the same id, and ids have to be unique. So remove the ids, and target the classes instead, and use forEach to iterate over each of them to remove the class.
const timeout = setTimeout(loading, 3000);
function loading() {
const skeletons = document.querySelectorAll('.skeleton');
skeletons.forEach(skeleton => {
skeleton.classList.remove('skeleton');
});
}
.skeleton { color: red; }
<div class="skeleton">One</div>
<div class="skeleton">Two</div>
<div class="skeleton">Three</div>
<div class="skeleton">Four</div>
You are calling getElmentById on Class. Can You Share The HTML Elment whose id or class is skeleton
try this
function loading() {
const element = document.getElementsByClassName("skeleton")[0];
element.classList.remove("skeleton");
}
I think the reason behind its not working is that your trying to remove the skeleton class from the skeleton itself. Try targeting the parent Element of the skeleton and then remove the skeleton from the parent Element. Did you try using :
const parentNode=document.querySelector(".parentElem");
parentNode.removeChild(document.querySelector(".skeleton"));
Did you notice you are trying to get an element by using getElementById whereas you stated skeleton is a class.

Get element data-attributes and insert them into the button

I want to ensure that when I click on the divs (A, B, C), the link of the button changes and gets the values of the data attributes in the appropriate places. I wrote a small script, but it does not work, and there is still not enough knowledge to understand exactly where I went wrong. Any help would be welcome.
document.getElementById("product").onclick = function() {
document.getElementById("purchase").href =
"/?add-to-cart=" + this.data-product +
"&variation_id=" + this.data-id + "/";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="product__items" id="product">
<div data-id="338" data-product="A" id="uI-1" class="items-uniqueItem">A</div>
<div data-id="339" data-product="B" id="uI-2" class="items-uniqueItem">B</div>
<div data-id="340" data-product="C" id="uI-3" class="items-uniqueItem">C</div>
<div class="product__items---btn">
Button
</div><!-- btn -->
</div>
You have several problems here.
First, I suggest you consult the documentation for HTMLElement.dataset or jQuery's .data().
Also, if you intend on using event delegation, you can't use this to refer to the event source element in a vanilla event listener as it will refer to the delegate.
Since you do have jQuery involved, you might as well use it since it makes this a lot easier (see also vanilla JS version below)
const button = $("#purchase")
$("#product").on("click", ".items-uniqueItem[data-id][data-product]", function() {
// Due to the selector above, `this` is now the clicked `<div>`
// Extract data properties
const { product, id } = $(this).data()
// Construct URL parameters
const params = new URLSearchParams({
"add-to-cart": product,
"variation_id": id
})
// Set the `href`
button.prop("href", `/?${params}/`)
})
/* this is just for visibility */
.items-uniqueItem{cursor:pointer;}#purchase{display:block;text-decoration:none;margin: 1rem;}#purchase:after{content:attr(href);display:block;color:#ccc;margin:.5rem;}
<!-- your HTML, just minified -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><div class="product__items" id="product"><div data-id="338" data-product="A" id="uI-1" class="items-uniqueItem">A</div><div data-id="339" data-product="B" id="uI-2" class="items-uniqueItem">B</div><div data-id="340" data-product="C" id="uI-3" class="items-uniqueItem">C</div><div class="product__items---btn">Button</div></div>
A vanilla JS version would look something more like this. You can use Element.closest() to locate the delegated event source
const button = document.getElementById("purchase")
document.getElementById("product").addEventListener("click", e => {
// find the required event source element
const el = e.target.closest(".items-uniqueItem[data-id][data-product]")
if (el) {
// Extract data properties
const { product, id } = el.dataset
// Construct URL parameters
const params = new URLSearchParams({
"add-to-cart": product,
"variation_id": id
})
// Set the `href`
button.href = `/?${params}/`
}
})
.items-uniqueItem{cursor:pointer;}#purchase{display:block;text-decoration:none;margin: 1rem;}#purchase:after{content:attr(href);display:block;color:#ccc;margin:.5rem;}
<!-- your HTML, just minified -->
<div class="product__items" id="product"><div data-id="338" data-product="A" id="uI-1" class="items-uniqueItem">A</div><div data-id="339" data-product="B" id="uI-2" class="items-uniqueItem">B</div><div data-id="340" data-product="C" id="uI-3" class="items-uniqueItem">C</div><div class="product__items---btn">Button</div></div>
As you can see, it's not very different to the jQuery version so maybe you might not need jQuery
I've never personally used the element.onlick = function() {...} notation, so I'll be usingelement.addEventListener('click', (e) => ...), but it should work the same way.
What you are doing is selecting the object that has the id "product". But "product" is the parent os the elements you want to select.
If you want to select several elements and do something with them, you can't use the id attribute, since id is unique for html page. So you'll want to use classes for that.
Create a class and add that class to each child (the ones with the data-product).
Select all children with .querySelectorAll(). Here is the doc. This returns a NodeList, but it's similar to an Array.
Iterate thought the List with a .forEach(item => ...) where item represents each element of the list.
Add an Event Listener (or .click, I guess) on each item.
*theList*.forEach( (item) => {
item.addEventListener('click', (event) => {
event.target.href = "/?add-to-cart=" + event.target.dataset.product + "&" + "variation_id=" + event.target.dataset.id + "/";
})
));
To access a dataset in JS you use the .dataset property.
First, grab all the divs that have a given class so that we can use their data.
const items = document.querySelectorAll('.items-uniqueItem');
items.forEach(item => item.addEventListener('click', (e) => console.log(e.target)))
Then inside you click handler you can get the button reference and assign the properties you want to get from it.

I want to add the "is-active" class in the active slide and the "is-out" class in the slide that comes out

I have the following HTML document which is a SlideShow :
<div class="ReferencesCarousel">
<div class="ReferencesCard"></div>
<div class="ReferencesCard"></div>
<div class="ReferencesCard"></div>
</div>
The javascript code relating to the code below is as follows:
var ReferenceIndex = 0;
showReferences();
function showReferences() {
var i;
var References = document.getElementsByClassName("ReferencesCard");
for (i = 0; i < References.length; i++) {
References[i].classList.remove('is-active');
}
ReferenceIndex++;
if (ReferenceIndex > References.length) {ReferenceIndex = 1}
References[ReferenceIndex-1].classList.add('is-active');
setTimeout(showReferences, 2000);
}
What I want to do is add the "is-out" class in the slide that will come out.
That means each class that contains "is-active" changes to "is-out" and the "is-out" class will be added to the next slide.
Another thing: the is-out class should last 1 second and it should be deleted.
You can use querySelector to find the node that has the .isActive class. Then you can remove it from the node where it occurs.
It will looking something like this:
let isActive = document.querySelector('.is-active')
if (isActive !== null){
isActive.classList.remove('is-active')
The setInterval method will take care of your timing issue. It will run a function after a set amount of time determined by you. Read more here.
Instead of running a for loop, you can set i = 0, like you did above. Then within your setInterval function, have:
i++
References[i].classList.add('is-active')
All of these together will 1) search for any nodes with the 'is-active' class 2)remove it, 3) add the 'is-active' class to the node in your list with index value equal to i in that interval.

Passing in right information when rendering items from Javascript on the HTML side

I am trying to create an extension for VSTS using their extension kit (https://learn.microsoft.com/en-us/vsts/extend/overview?view=vsts).
<script type="text/javascript">
VSS.init();
var items = {}
// Get data service and display
VSS.getService(VSS.ServiceIds.ExtensionData).then((dataService) => {
dataService.getDocuments('MyCollection2').then((docs) => {
// keep a reference to the element instead of searching for it in each loop.
const itemsDiv = document.getElementById('items');
const contents = [];
for (let i = 0; i < docs.length; i++) {
// using template strings here to show you another way of working with strings in es6
var name = docs[i].name
contents.push(
`<div
class="listItem"
onClick="console.log(docs[i])"
onmouseover="this.style.background='#D5DBDB';"
onmouseout="this.style.background='white';">
${docs[i].name}
</div>`
)
}
// finally update the target element one time with your contents.
// The new line character isn't required, can just use '',
// but this might be easier to read for you
itemsDiv.innerHTML = contents.join('');
});
});
</script>
So what my javascript part does is I try to fetch objects from VSTS`s internal data storage (I named it MyCollection2) and display the objects as a list
HTML part
<section>
<nav>
<div class="create_button">+ Create KPI</div>
<div id="items"></div>
</nav>
<article>
<h2>Create KPI</h2>
<br>
<form action="" id="form" onsubmit="sConsole(event)">
KPI Name<br>
<input type="data" id="name">
<br><br>
Actual Value<br>
<input type="data" id="actual">
<br><br>
Potential Value<br>
<input type="data" id="potential">
<br><br>
Goal %<br>
<input type="data" id="goal">
<br><br>
<button type="submit">Create</button><span>Cancel</span>
</form>
</article>
</section>
So all the objects are rendered in the div with the id items.
Everything is fine up to this point.
The problem is the onClick="console.log(docs[i]) part in my javascript part.
My intention was to console.log the document object whenever each item in the list was clicked.
However, this doesn't print the object as I intended.
It just prints externalContentHost10 and I don't know what that is.
What can I do to make this work?
docs is defined in your function; the scope of the onclick attribute (note: should be all lowercase) is not the same. In general, you should avoid inline event handlers as they’re not very flexible or maintainable. You should instead use addEventListener, which means ditching innerHTML and working with proper element nodes. A few other changes I would make are:
Flattening the promises (removing the nesting) by returning them
Using for...of for iteration
Using const (and let, but in this case const is enough) instead of var so that your variables have the right scope
This gives us:
VSS.init();
const items = {};
// Get data service and display
VSS.getService(VSS.ServiceIds.ExtensionData)
// the callback on the next line returns a promise, which the JavaScript engine will follow, so you don't need to nest the next `then`
.then((dataService) => dataService.getDocuments('MyCollection2'))
.then((docs) => {
// keep a reference to the element instead of searching for it in each loop.
const container = document.getElementById('items');
// this loop will remove any existing children
while (container.firstChild !== null) {
container.removeChild(container.firstChild);
}
// `for...of` is a simpler way to iterate over a collection
for (const doc of docs) {
// create a `div` element
const div = document.createElement("div");
// add a text node to it
div.appendChild(document.createTextNode(doc.name));
// add event listeners to change its background
div.addEventListener("mouseover", e => { div.style.background = "#D5DBDB"; });
div.addEventListener("mouseout", e => { div.style.background = "white"; });
// add a `click` listener
div.addEventListener("click", e => { console.log(doc); });
// add the new div to the container
container.appendChild(div);
}
});
If you wanted to use classes instead to manage the styling—which is the recommended method—then you could implement the event listeners using classList:
div.addEventListener("mouseover", e => div.classList.add("hover-class"));
div.addEventListener("mouseout", e => div.classList.remove("hover-class"));
(classList has toggle and replace methods, but they aren’t supported by IE at all, and Edge only seems to support toggle, so whether to use them depends on your minimum supported version.)
But you would probably be better off defining a CSS :hover class rather than doing all this, if styling is all you want to change.

javascript - avoid repetable code

I have a page with a button that calls a menu modal. The modal contains two more buttons that call two submenus - one for each button. Watch the pen:
https://codepen.io/t411tocreate/pen/yoxJGO
It actually works. But the current problem is that I re-write a repeatable code to call each submenu:
$('.show-submenu-1').on('click', function () {
$('.submenu-1.offcanvas').addClass('offcanvas--active');
})
$('.show-submenu-2').on('click', function () {
$('.submenu-2.offcanvas').addClass('offcanvas--active');
})
This approach seems to be pretty dumb. I need a solution with less repetition, something like forEach function for arrays:
var menus = [
'.show-submenu-1',
'.show-submenu-2'
];
menus.forEach(function(menu){
$(menu).on('click', function () {
$(`${menu}.offcanvas`).addClass('offcanvas--active');
})
});
Of course, this scenario won't work. How can I make my code DRY?
Use markup:
<div class="submenu" data-index="1">
<div class="submenu" data-index="2">
<button class="show-submenu-button" data-submenu-index="1">
<button class="show-submenu-button" data-submenu-index="2">
Then:
$('.show-submenu-button').on('click', function () {
var index = $(this).attr('data-submenu-index');
$('.submenu[data-index="' + index + '"]').addClass('offcanvas--active');
})
There is little value to using classnames that are so specific that they identify every element on the page individually. Classnames should define a class of elements that behave the same way.
Hi I hope I got the question right but you could use data-attributes for something like this. Just set a general class for .show-submenu and mark their connection to the menus with a number in a data-submenu=x attribute. Where x would be the number in .submenu-x.
And then you do something like this:
Notice that i changed .show-submenu-1 to .show-submenu. Make sure every trigger has this class. Also add a data-submenu=x for every submenu you want to use.
$('.show-submenu').on('click', function () {
var number = $(this).attr("data-submenu");
var selector = '.submenu-' + number + '.offcanvas'
$(selector).addClass('offcanvas--active');
})
So the data-submenu is used to pair the trigger and the modal. This way you can stick to an easy to read html code and a short bit of jquery.
Try this:
var menus = [1, 2];
menus.forEach(index => {
$(`.show-submenu-${index}`).on('click', () => {
$(`.submenu-${index}.offcanvas`).addClass('offcanvas--active');
});
});
You can use this as well.
$('.show-submenu-1, .show-submenu-2').on('click', function (event) {
$(event.target).hasClass('show-submenu-1'){
$('.submenu-1.offcanvas').addClass('offcanvas--active');
}else{
$('.submenu-2.offcanvas').addClass('offcanvas--active');
}
})
it would be better to have your show-submenu-1(as showmenu) and submenu-1(as submenu) in same parent element that allows you to use closest() method and make life easy
for eg:
$('.show-submenu').on('click', function (event) {
$(event.target).closest('.submenu').addClass('offcanvas--active');
})

Categories