I want to traverse through pages and toggle active class through them. How should I do this without using set class?
HTML
<div class="page active"></div>
<div class="set">
<div class="page"></div>
<div class="page"></div>
</div>
<div class="page"></div>
jQuery
$('.active').toggleClass('active').toggle().nextAll('.page').toggleClass('active');
I am assuming that by "traverse" you mean you want to toggle the .page divs one by one in a certain order.
If that is the case, write an algorithm that traverses a tree: given a root, toggle if it is a .page, and recursively deal with each of its children
function traverse(root){
if(!root) return;
if(root.hasClass('page')) root.toggle('active');
root.children().forEach(function(child){
traverse(child);
});
//lets say you want to bind to click event on every div
$('div').click(function(){
traverse($(this));
});
}
Unfortunately we don't have a direct way to find the next non sibling element, but we can handle that situation in many ways using jquery functions. I just tried on way to achieve your goal, check out this working fiddle and let me know if you need any clarity, added some inline comments also for your understanding.
HTML:
<div class="page active">div 1</div>
<div class="page">div 2</div>
<div class="set">
<div class="page">set 1 - div 1</div>
<div class="page">set 1 - div 2</div>
<div class="page">set 1 - div 3</div>
</div>
<div class="page">div 5</div>
<div class="set">
<div class="page">set 2 - div 1</div>
<div class="page">set 2 - div 2</div>
</div>
<div class="page">div 6</div>
<button class="next-btn">Next</button>
CSS:
.active {
color: red;
}
.next-btn {
cursor: pointer;
}
Javascript:
$(function() {
$('button').click(function() {
var elem = $(".page.active").toggleClass('active'); // current active element
var nextElem = elem.next(); // next element
// go above one level and get next element
// if no next element exists, means end of the child level
if (!nextElem.length) {
nextElem = elem.parent().next();
}
// if next element has some PAGE children then update the first child element
if (nextElem.children('.page').length > 0 ) {
nextElem.children('.page:first-child').toggleClass('active');
} else if (nextElem.hasClass('page')) {
nextElem.toggleClass('active');
}
});
});
This approach handles the scenario with one child level, you can extend this to multiple levels also with recursive functions, I think this helps you to handle your scenario accordingly.
Working fiddle
You could achieve that using indexes to get the next element in the DOM using the index of active one +1 then active it, I think the following is what you are looking for:
var getActiveIndex = function(){
var active_index;
$('.page').each(function(i){
if ( $(this).hasClass('active') )
active_index = i;
})
return active_index;
}
$('body').on('click', '.next', function(){
var active_page_index = getActiveIndex(); //Get active page index
var new_index = active_page_index+1; //Set the next page index
var next_page = $('.page:eq('+new_index+')'); //Get the next page
$('.page').removeClass('active');
if(next_page.length)
next_page.addClass('active');
else
$('.page:first').addClass('active');
})
I hope this helps.
var getActiveIndex = function(){
var active_index;
$('.page').each(function(i){
if ( $(this).hasClass('active') )
active_index = i;
})
return active_index;
}
$('body').on('click', '.next', function(){
var active_page_index=getActiveIndex();
var new_index = active_page_index+1;
var next_page = $('.page:eq('+new_index+')');
$('.page').removeClass('active');
if(next_page.length)
next_page.addClass('active');
else
$('.page:first').addClass('active');
})
.active{
background-color: red;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="page active">page</div>
<div class="set">
<div class="page">- Set page</div>
<div class="page">- Set page</div>
</div>
<div class="page">page</div>
<div class="page">page</div>
<div class="page">page</div>
<div class="set">
<div class="page">- Set page</div>
<div class="page">- Set page</div>
</div>
<div class="page">page</div>
<br>
<button class='next'>Active the next page</button>
Related
I would like to make 3 buttons with each one make all the content div to display: none and depending on the button you have click one of the content div change to display: block. For example, If I click on the second button It will show only the second div content.
function showPanel(id) {
var elements = document.getElementsByClassName("content");
for (let i = 0; i < elements.length; i++) {
document.getElementById(i).style.display = "none";
}
document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('1')">test1</button>
<button onclick="showPanel('2')">test2</button>
<button onclick="showPanel('3')">test3</button>
<div class="content">
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p class="other">TEST2</p>
</div>
<div id="3" class="content ">
<p class="other">TEST3</p>
</div>
</div>
There's a couple of issues in your code. Firstly length is a property, not a method, so you don't need the () suffix to invoke it. Secondly, there's no className attribute in HTML. This should just be class. Lastly the parent container shares the same class as the elements you're hiding, so all the child elements get hidden, even if they have display: block applied to them.
With these issues corrected, your code would look like this:
function showPanel(id) {
var elements = document.getElementsByClassName("panel");
for (let i = 0; i < elements.length; i++) {
elements[i].style.display = "none";
}
document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('p1')">test1</button>
<button onclick="showPanel('p2')">test2</button>
<button onclick="showPanel('p3')">test3</button>
<div class="content">
<div id="p1" class="panel">
<p>TEST1</p>
</div>
<div id="p2" class="panel">
<p class="other">TEST2</p>
</div>
<div id="p3" class="panel">
<p class="other">TEST3</p>
</div>
</div>
However it's worth noting that using onX attributes is outdated and not good practice. A better solution would be to use unobtrusive event handlers and provide custom metadata to the event handler through data attributes placed on the elements.
The improved version of the logic would look like this:
let buttons = document.querySelectorAll('button');
let panels = document.querySelectorAll('.panel');
buttons.forEach(button => {
button.addEventListener('click', e => {
panels.forEach(panel => {
panel.style.display = panel.id === e.target.dataset.panel ? 'block' : 'none';
});
});
});
<button data-panel="1">test1</button>
<button data-panel="2">test2</button>
<button data-panel="3">test3</button>
<div class="content">
<div id="1" class="panel">
<p>TEST1</p>
</div>
<div id="2" class="panel">
<p class="other">TEST2</p>
</div>
<div id="3" class="panel">
<p class="other">TEST3</p>
</div>
</div>
No need for JS or Jquery. Instead of a button you can use an anchor tag. Then you calling with the anchor the id of the element. Last but not least you make the boxes hidden through CSS and use the :target selector to display the elements:
.content {
display: none;
}
.content:target {
display: block;
}
test1<br>
test2<br>
test3<br>
<div class="content-container">
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p class="other">TEST2</p>
</div>
<div id="3" class="content ">
<p class="other">TEST3</p>
</div>
</div>
Multiple issues.
Length can be calculated using elements.length and not elements.length()
You have given same class name to both the parent and the child divs. So hiding all elements with class name content will hide your whole parents itself. So after updating style.display = "block" to the required target, it will not work. Because your parent is already style.display = "none". So you should make a logic update there. So I changed the parent class name.
function showPanel(id) {
var elements = document.getElementsByClassName("content");
for (let i = 0; i < elements.length; i++) {
elements[i].style.display = "none";
}
document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('1')">test1</button>
<button onclick="showPanel('2')">test2</button>
<button onclick="showPanel('3')">test3</button>
<div>
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p class="other">TEST2</p>
</div>
<div id="3" class="content ">
<p class="other">TEST3</p>
</div>
</div>
A more elegant way I might approach a prob,problem like this would be to tie the panels and their triggers together using data-attributes. This way, you don't risk conflicts with other IDs that m ay be the same on the page (IDs should always be unique).
Before setting up my event listener, I would initialize an openPanel variable and set it to any panel that is already created with the active class name. Whenever we open a new panel, we will overwrite this variable vaklue, so we don't need to do a new querySelctor each time.
Then, in the CSS, rather than hiding all panels and then showing the one with the active class, we can write a single style that hides any panels without the active class using the :not negation selector.
This is how that would look (initializing this with panel #1 open by default, but you can simply remove the active class from it in the HTML if you don't want that):
let openPanel = document.querySelector('[data-panel-id].active');
document.addEventListener('click', e => {
if (e.target?.matches?.('[data-panel-target]')) {
const id = e.target.dataset.panelTarget;
if (id) {
const panel = document.querySelector(`[data-panel-id="${id}"]`);
if (panel) {
openPanel?.classList.remove('active');
panel.classList.add('active');
openPanel = panel;
}
}
}
})
[data-panel-id]:not(.active) {
display: none;
}
<button data-panel-target="1">test1</button>
<button data-panel-target="2">test2</button>
<button data-panel-target="3">test3</button>
<main>
<div data-panel-id="1" class="active">
<p>TEST #1</p>
</div>
<div data-panel-id="2">
<p>TEST #2</p>
</div>
<div data-panel-id="3">
<p>TEST #3</p>
</div>
</main>
I already submitted a separate solution with my preferred recommendation, but I wanted to provide an answer to your question using the same approach you started with so as not to deviate from the code you already have in place.
The code you already had in place was actually fairly close to working already. The main issue I saw was that you were using document.getElementById(i) where you should actually have been using elements[i]. We can improve this further though, by replacing the for loop with a for..of loop, and determining inline whether the current element being evaluated is the one we want to show. If so, we use 'block', otherwise 'none'.
After initializing our function, we can call it on one of our IDs within the JS to have one panel open by default. **It's also important that the parent of all these .content elements NOT contain the class name content as well, as that would conflict with your function. I have replaced that parent element with a simple <main>…</main> element.
Here is how I would achieve solving this using your existing approach:
function showPanel(contentId) {
const elements = Array.from(document.getElementsByClassName('content'));
for (const element of elements) {
element.style.display = element.id === contentId ? 'block' : 'none';
}
}
showPanel('1');
<button onclick="showPanel('1')">test1</button>
<button onclick="showPanel('2')">test2</button>
<button onclick="showPanel('3')">test3</button>
<main>
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p>TEST2</p>
</div>
<div id="3" class="content ">
<p>TEST3</p>
</div>
</main>
i have this html collection, i want if i click on any div class ".sday"
any other div that are present after that be remove .
for example if we click on sday 2 we should keep sday1 and sday 2, and 3 and 4 must delete
my script removing count is ok but it delete wrong div.
any idea?
<div id="parent" class="parent">
<div class="room-sheet">
<div class="sday">1</div>
</div>
<div class="room-sheet">
<div class="sday">2</div>
</div>
<div class="room-sheet">
<div class="sday">3</div>
</div>
<div class="room-sheet">
<div class="sday">4</div>
</div>
</div>
script(using jquery)
<script>
$(".sday").click(function(){
console.log("hello");
var parentElement = $(this).parent().parent().find('.room-sheet');
var parentChildernCount = $(this).parent().parent().find('.room-sheet').length;
var elementIndex = $(this).closest('.room-sheet').index();
var dd = parentChildernCount - elementIndex;
for(let i=elementIndex; i < dd; i++){
//note: make sure any element after our index in deleted!!!
$("#parent").find('.room-sheet').children().eq(i).remove();
}
})
</script>
Listen for clicks on a .sday on the parent, navigate to the parent of the clicked .sday (a .room-sheet), call nextAll to get all subsequent siblings, and .remove() them:
$('#parent').on('click', '.sday', function() {
$(this).parent().nextAll().remove();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="parent" class="parent">
<div class="room-sheet">
<div class="sday">1</div>
</div>
<div class="room-sheet">
<div class="sday">2</div>
</div>
<div class="room-sheet">
<div class="sday">3</div>
</div>
<div class="room-sheet">
<div class="sday">4</div>
</div>
</div>
Also, there's no need to require a big library like jQuery for something this simple, you may implement this with native DOM methods, if you like:
document.querySelector('#parent').addEventListener('click', ({ target }) => {
if (!target.matches('.sday')) {
return;
}
const sheet = target.parentElement;
while (sheet.nextSibling) {
sheet.nextSibling.remove();
}
});
<div id="parent" class="parent">
<div class="room-sheet">
<div class="sday">1</div>
</div>
<div class="room-sheet">
<div class="sday">2</div>
</div>
<div class="room-sheet">
<div class="sday">3</div>
</div>
<div class="room-sheet">
<div class="sday">4</div>
</div>
</div>
Save the current number and the maximum number in variables then just iterate through them, making sure not to delete the clicked one:
$(".sday").on("click", function() {
let start = parseInt($(this).text());
let finish = parseInt($(".sday").last().text());
for (let i = start + 1; i <= finish; i++) {
$(`.sday:conatins(${i})`).remove();
}
});
When I click a button div, I want to add a class named active to the popup div that has the corresponding class name in the data-trigger attribute.
For example if I click on the div with class name button-two, the div with data-trigger="button-two" should get class active.
The issue: active is added only to the last popup div. How can I make this work?
Here's what I have tried:
$('.popup').each(function() {
popupObj = $(this);
var popupTrigger = popupObj.data("trigger");
$('.' + popupTrigger).click(function() {
popupObj.addClass('active');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="button-one">Test 1</div>
<div class="button-two">Test 2</div>
<div class="button-three">Test 3</div>
<div class="popup" data-trigger="button-one">Hello world</div>
<div class="popup" data-trigger="button-two">Hello there</div>
<div class="popup" data-trigger="button-three">Hello again</div>
This is because you did not declare popupObj as a local variable; It is global, and so it will have changed in the last iteration of the each: it is that value that will be referenced in all three click handlers.
Remember that each will have performed all iterations before any of the click handlers get called.
Solution: use var popupObj. That way each of the click handlers will reference their "own" variable.
Simple way to use data attribute also for buttons .. and make only one click event for the buttons .. see the next example
$('.button[data-to-trigger]').on('click' , function(){
var GetTriggerDiv = $(this).attr('data-to-trigger');
$('.popup').removeClass('active').filter('.popup[data-trigger="'+GetTriggerDiv+'"]').addClass('active');
});
.active{
background : red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="button" data-to-trigger="button-one">Test 1</div>
<div class="button" data-to-trigger="button-two">Test 2</div>
<div class="button" data-to-trigger="button-three">Test 3</div>
<div class="popup" data-trigger="button-one">Hello world</div>
<div class="popup" data-trigger="button-two">Hello there</div>
<div class="popup" data-trigger="button-three">Hello again</div>
The "each" function in jQuery returns 2 values: index and element. Try using the element returned instead of $(this).
$('.popup').each(function(index, element) {
var popupTrigger = element.data("trigger");
$('.'+popupTrigger).click(function() {
element.addClass('active');
});
});
Here's a solution that matches the popup to the button using the same data trigger for both:
$(document).ready(function() {
$(".btn").click(function() {
var trigger = $(this).data("trigger");
$('.popup').each(function() {
if ($(this).data("trigger") == trigger) {
$(this).addClass('active');
} else {
$(this).removeClass('active');
}
});
});
});
.active {
background-color: coral;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="btn" data-trigger="button-one">Test 1</div>
<div class="btn" data-trigger="button-two">Test 2</div>
<div class="btn" data-trigger="button-three">Test 3</div>
<div class="popup" data-trigger="button-one">Hello world</div>
<div class="popup" data-trigger="button-two">Hello there</div>
<div class="popup" data-trigger="button-three">Hello again</div>
I have a layout like this and I need to reach the AreaForMapsn nodes and hide them
This is my HTML:
<div id="layout1" class="layout1_wrapper">
<div class="grid">
<div class="block">
<div id="AreaForMaps1" name="AreaForMaps1">
<div id="googlemaps1">
</div>
</div>
</div>
</div>
<div class="grid">
<div class="block">
<div id="AreaForMaps2" name="AreaForMaps2">
<div id="googlemaps2">
</div>
</div>
</div>
</div>
<div class="grid">
<div class="block">
<div id="AreaForMaps3" name="AreaForMaps3">
<div id="googlemaps3">
</div>
</div>
</div>
</div>
</div>
JavaScript:
<script>
document.getElementById("Button").onclick = function(){
//need to reach all AreaForMapsXXX divs and hide them
var myDiv = document.getElementById( "layout1" );
var children = document.getElementById(layout1).childNodes;
for(i=0; i<children.length; i+=3) {
document.getElementById(children[i].id).style.display = "none";
}
}
</script>
One possible way is to add a class, say "AreaForMaps", to all of them and then simply use document.getElementsByClassName (or jQuery or whatever you're using).
There are several ways to go about it. Here's one way.
document.getElementById("Button").onclick = function(){
var myDiv = document.getElementById( "layout1" );
var divs = myDiv.getElementsByTagName("div");
for(i=0; i<divs.length; i++) {
if (divs[i].id.indexOf("AreaForMaps") === 0)
divs[i].style.display = "none";
}
}
Your code was only targeting nodes that are immediate children of "layout1", and was including text nodes (the tab and newline characters are represented as nodes in the DOM).
This code gets all div elements that descend from "layout1", and verifies that the first part of their ID starts with "AreaForMaps" before hiding it.
If the list of browsers you support only include IE8 and higher, you can do this instead:
document.getElementById("Button").onclick = function(){
var divs = document.querySelectorAll( "#layout1 div[id^=AreaForMaps]" );
for(i=0; i<divs.length; i++)
divs[i].style.display = "none";
}
I have divs with same class, but each 3 are wrapped in a parent div. I can't get the index the way I want it. I am sorry, could anyone help me to get the index as number from 0 to 8.. when i click on any element? despite the parent element.
Here is my full code for your testing.
<div class="more-content">
<div class="post">post 1</div>
<div class="post">post 2</div>
<div class="post">post 3</div>
</div>
<div class="more-content">
<div class="post">post 4</div>
<div class="post">post 5</div>
<div class="post">post 6</div>
</div>
<div class="more-content">
<div class="post">post 7</div>
<div class="post">post 8</div>
<div class="post">post 9</div>
</div>
<script type="text/javascript">
$(function() {
// navigate posts with next/prev buttons
$(".post").click(function(){
alert($(this).index());
});
});
</script>
If i click i get index 0, 1, 2 ..i think because each 3 items are wrapped in parent? I am new with jquery, any help appreciated. what i need is get the index of post with same class.. say post 6 = index 5.. and so on
UPDATE
How can I get the same result if the clicked element is a child anchor in the post div and not the post div directly?
Try index method with this as an argument:
$(".post").click(function() {
alert($(".post").index(this));
});
DEMO: http://jsfiddle.net/Nryf3/
var posts = $('.post').click(function(){
alert(posts.index(this));
});
DEMO: http://jsfiddle.net/paZ2e/
You could loop through all elements tagged post and increment a counter until you find the object you're looking for, like this:
$(".post").click(function() {
var counter = 0;
var that = this;
$(".post").each(function() {
if(that == this) break;
counter++;
}
// counter now equals the index of the post element out of all posts on the page
});