I am trying to do logic using javascript, so that if div where class is b-format if innerhtml value is Audio it will hide cart-button div, else it will hide the more-button div. For some reason its not working.
var itemButtons = document.querySelectorAll('.slider-wrapper');
for(var i=0; i<itemButtons.length; i++){
var b_format_element = itemButtons[i].getElementsByClassName("b-format")[0];
var cart_element = itemButtons[i].getElementsByClassName("cart-button")[0];
var more_element = itemButtons[i].getElementsByClassName("more-button")[0];
if(b_format_element.innerHTML == "Audio"){
cart_element.style.display = "none";
} else {
more_element.style.display = "none";
}
}
this is html code
<div class="slider-wrapper">
${#Recommend} // this repeat record
<a class="product" href="#">
<div>
<p class="b-format">Audio</p>
</div>
<div class="product-items cart-button">
<span>Read more</span>
</div>
<div class="product actions product-items more-button">
<span>Read more</span>
</div>
</a>
${/Recommend}
</div>
Not good idea to use the same ID tags over and over in a loop. Instead, use a class name. Also, using querySelector will get you the first matching element. It also looks like you want to cycle through the inner DIVs of the slider-container, rather than cycling through multiple slider containers. I added an inner container .record.
document.querySelectorAll('.slider-wrapper .record').forEach(c => {
let isAudio = c.querySelector('.b-format')?.innerText.trim() === 'Audio';
c.querySelector('.cart-button').style.display = isAudio ? 'none' : 'block';
c.querySelector('.more-button').style.display = !isAudio ? 'none' : 'block';
})
.cart-button {
color: #f00;
}
.more-button {
color: #999;
}
<div class="slider-wrapper">
<div class='record'>
<div>
<p class="b-format">Audio</p>
</div>
<!-- buttom -->
<div class="cart-button">
<span>Les mer</span>
</div>
<div class="more-button">
<span>Les mer</span>
</div>
<!-- button end -->
</div>
<div class='record'>
<div>
<p class="b-format">Not Audio</p>
</div>
<!-- buttom -->
<div class="cart-button">
<span>Les mer</span>
</div>
<div class="more-button">
<span>Les mer</span>
</div>
<!-- button end -->
</div>
</div>
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>
We have issues with our code:
function remove_single_entry_if_empty() {
$(".single-entry").each(function() {
var ids = $(this).attr('id');
let a = (ids);
for ( let i = 0; i < a.length; a++ ) {
let x = document.getElementById(a);
if ( x.getElementsByClassName('entry_times-wrapper').length === 1 ) {
var c = x.getElementsByClassName('entry_times-wrapper').length === 1;
x.style.display = 'none';
}
}
});
}
HTML Structure:
<div class="single-entry" id="9127">
<div class="entries_wrapper">
<div class="entry_times-wrapper">
<!-- this is where the <a> tags is. -->
</div>
</div>
</div>
We have an HTML tag with the class single-entry. This class exist multiple times but each with a unique ID specified. The class name called entry_times-wrapper (which is a child element of variable X) has also multiple <a> tags.
What we want to do: if all items in class entry_times-wrapper are hidden (with display none), then hide the single-entry class for only that specific ID. For now, this code as described above will actually hide all these single entries.
How can we do this correctly?
To achieve this you can use a single line of jQuery which selects all the .single-entry elements which contain a hidden .entry_times-wrapper and then hide them. Try this:
$('.single-entry:has(.entry_times-wrapper:hidden)').hide();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="single-entry" id="9127">
<div class="entries_wrapper">
<div class="entry_times-wrapper">
Visible...
</div>
</div>
</div>
<div class="single-entry" id="9128">
<div class="entries_wrapper">
<div class="entry_times-wrapper">
Not visible...
</div>
</div>
</div>
<div class="single-entry" id="9120">
<div class="entries_wrapper">
<div class="entry_times-wrapper">
Visible...
</div>
</div>
</div>
Here is a working demo with several <div>s and <a>s:
$("button").click(function(){ $(".single-entry").add("a").show();});
$(".entry_times-wrapper a").click(function(){ $(this).hide();checkVis();})
// this needs to be called every time an <a> element is hidden or made visible again:
function checkVis(){
$(".single-entry").each(function(){
$(this).toggle($(".entry_times-wrapper a:visible",this).length>0)
});
}
.single-entry {background-color:#ccc; padding:6px;
border: 1px black solid; margin: 4px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>show all links again</button><br>
<div class="single-entry" id="9127">
<div class="entries_wrapper">
<div class="entry_times-wrapper">
first link<br>
second link<br>
third link
</div>
</div>
</div>
<div class="single-entry" id="9128">
<div class="entries_wrapper">
<div class="entry_times-wrapper">
fourth link<br>
fifth link<br>
sixth link
</div>
</div>
</div>
<div class="single-entry" id="9129">
<div class="entries_wrapper">
<div class="entry_times-wrapper">
seventh link<br>
eighth link<br>
ninth link
</div>
</div>
</div>
I want the elements to be appended to another elements - they have to
be found in a div class that have id="parent".
the code must be only in clean javascript. The problem is, why i cant use just append - children append only for 1 element(first block) so need a loop that will get every element(2) to append to the (1) element.
<!-- FirstBlock -->
<div id="parent">
<div id="firstElement">
</div>
<div id="secondElement">
</div>
</div>
I want it to look like this
<!-- FirstBlock -->
<div id="parent">
<div id="firstElement">
<div id="secondElement">
</div>
</div>
</div>
Here is the code i tried:
function getElement() {
var firstElement = document.getElementById('firstElement');
var secondElement = document.getElementById('secondElement');
var parent = document.getgetElementById('parent').children;
for(var i = 0; i < parent.length; i++){
for(firstElement in parent[i]){
if(!(secondElement in firstElement){
firstElement.appendChild(secondElement);
});
};
};
Fiddle
With the ID's being the same, you need to use a class attribute instead or change the ID for each of those elements. Here's an example of how you could achieve your result(CSS for visual example).
function getElement() {
var parents = document.getElementsByClassName('parent'),
parentsLength = parents.length;
while (parentsLength--){
var firstElements = parents[parentsLength].getElementsByClassName('firstElement'),
firstElementsLength = firstElements.length,
secondElements = parents[parentsLength].getElementsByClassName('secondElement');
while(firstElementsLength--) {
var secondElementsLength = secondElements.length;
while(secondElementsLength--) {
firstElements[firstElementsLength].appendChild(secondElements[secondElementsLength]);
}
}
};
}
getElement();
.firstElement {
height: 50px;
border: 1px solid #000;
}
.secondElement {
background-color: #aaa;
}
<!-- FirstBlock -->
<div class="parent">
<div class="firstElement">
dddd
</div>
<div class="secondElement">
aaa
</div>
</div>
<!-- SecondBlock -->
<div class="parent">
<div class="firstElement">
firstElement
</div>
<div class="secondElement">
secondElement
</div>
</div>
<!-- ThirdBlock -->
<div class="parent">
<div class="firstElement">
firstElement
</div>
<div class="secondElement">
secondElement
</div>
</div>
I want it look like this
<!-- FirstBlock -->
<div class="parent">
<div class="firstElement">
firstElement
<div class="secondElement">
secondElement
</div>
</div>
</div>
<!-- SecondBlock -->
<div class="parent">
<div class="firstElement">
firstElement
<div class="secondElement">
secondElement
</div>
</div>
</div>
<!-- ThirdBlock -->
<div>
<div class="parent">
<div class="firstElement">
firstElement
<div class="secondElement">
secondElement
</div>
</div>
</div>
A quick rundown of the JavaScript..
We're getting each element with the parent class and setting a variable to the length of that object.
The while loop is running through each of those divs and getting the divs inside that contain the firstElement and secondElement classes.
Then more loops take each element with the class secondElement and insert them into the elements with the class firstElement.
At the end of it, for every parent div, any divs with a class of secondElement will be inserted into every div with a class of firstElement inside of that parent div.
Here is the fiddle that I went off of for modifications. Hope this helps!
Change your HTML code to have classes, instead of id's. There can't be more than one element with the same id.
<div class="parent">
<div class="firstElement">
</div>
<div class="secondElement">
</div>
</div>
Then use the following JavaScript:
var parents = document.getElementsByClassName("parent");
for(i = 0; i < parents.length; i++){
var current = parents[i];
var firstElement = current.getElementsByClassName("firstElement")[0];
var secondElement = current.getElementsByClassName("secondElement")[0];
firstElement.appendChild(secondElement);
current.removeChild(firstElement);
}
I am having one html page in which all navigation (lets say them the TABS) resides.
I wrote a javascript to show one and hide another one on link click.
It works well on the tab i want but when i navigate to another tabs that div is showing on the bottom. I dont want it to display it in all other tabs.
This is my javascript code:
function displayBlock(divName){
if(document.getElementById("vend")) {
var oldDiv = document.getElementById("vend");
oldDiv.style.display = 'none';
//show div
var newDiv = document.getElementById(divName);
newDiv.style.display = 'block';
}
else{
var newDiv = document.getElementById(divName);
newDiv.style.display = 'none';
}
}
How to modify it to meet the need?
HTML Structure:
<div class="tab-content">
<div class="tab-pane" id="dashboard">
<div class="container">
<!-- Container code -->
</div>
</div>
<div class="tab-pane" id="vend">
<div class="container">
<!-- Container code -->
<a id="auto-topup2" href="dashboard#auto-topup" onclick="displayBlock('schedule');">
Schedule Autovend
</a>
</div>
</div>
<div class="tab-pane" id="commision">
<div class="container">
<!-- Container code -->
</div>
</div>
<div id="schedule" style="display:none">
<div class="tab-pane" id="auto-topup">
<div class="container">
<!-- Container code -->
</div>
</div>
</div>
In the vend section i have created a whose onclick will call the function and hide the Div with vend id and show Div with schedule ID....But the DIV with scehdule ID is showing in all the tabs..
I hope now it can be somewhat undestandable
To hide all tabs without the selected one you can use this function:
function displayBlock(idOfBlock) {
document.getElementByClass('block').style.display = 'none';
document.getElementById(idOfBlock).style.display = 'block'
}
<div class="block" id="hello">
hello
</div>
<div class="block" id="foobar">
foobar
</div>
Basically I have multiple elements A,B,C,... And they are all "connected" to A1,B1,C1,...
For simplicity and better understanding , lets say A,B,C are personal data about A1,B1,C1 persons (A1,B1,C1 are pictures of those persons).
html looks like :
<div class="personal_data">
<p class="A"> Ronnie </p>
<p class="B"> James </p>
<p class="C"> Dio </p>
</div>
<div class="persons">
<div>
<div>
<div class="A1"> img1 </div>
</div>
</div>
<div>
<div>
<div class="B1"> img2 </div>
</div>
</div>
<div>
<div>
<div class="C1"> img3 </div>
</div>
</div>
</div>
Yes , those divs that contain img are nested like that and the order must not be changed.
p elements are hiddenand are shown in personal_data window according to which person has been clicked.
How can I make it that when one picture is clicked its corresponding p element is shown and the rest of them are hidden , and when I click to another picture it shows another p element and hides previous , and so on?
I tried with jQuery two methods :
$(".A1").click(function () {
$(".A").show();
$(".B").hide();
$(".C").hide(); })
But I immediately abandoned it for obvious reasons. It's ugly and I have more than 3 persons so doing it for every person like this would not be a good practice.
$(".persons div").click(function () {
var index=$(".persons div").index(this);
$(".personal_data p").hide().eq(index).show(); })
Because I don't know all jQuery functions ( and all native javascript functions) I was amazed by the power of these but because of those nested images the index of A that corresponds to the A1 would be ok , but other indexes would not have their pair with persons because the number od divs are not equal , rather then "shifted" by +3. So I tweaked .personal_data with 2 empty p elements after A,B and C so the indexes would align. And it worked but I feel like I am violating something .
Is there a more elegant way for achieving this? I feel my problem is lack of knowledge of all functions that exist inside javascript (and jQuery).
Get the list of matching clickable elements, and the personal data, ahead of time (so we don't have to keep re-querying them):
var clickables = $('.persons > div > div > div[class]');
var data = $('.personal_data p');
Then, when clicked, get the index of the clicked thing in that list, rather than hunt through the DOM:
clickables.click(
function() {
data.hide(); // hide the others
var idx = clickables.index(this);
$(data[idx]).show();
}
);
var clickables = $('.persons > div > div > div[class]');
var data = $('.personal_data p');
clickables.click(
function() {
data.hide();
var idx = clickables.index(this);
$(data[idx]).show();
}
)
.personal_data p {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="personal_data">
<p class="A">Ronnie</p>
<p class="B">James</p>
<p class="C">Dio</p>
</div>
<div class="persons">
<div>
<div>
<div class="A1">img1</div>
</div>
</div>
<div>
<div>
<div class="B1">img2</div>
</div>
</div>
<div>
<div>
<div class="C1">img3</div>
</div>
</div>
</div>
Assuming that you will have this class structure in your page consistently, I did something by comparing the class names. If the class name of the clicked div contains the class name of the p element, then the p will be shown, otherwise they will be hidden :
$(".persons div").click(function () {
var myclassname = $(this).attr('class');
$(".personal_data p").hide().filter(function() {
return myclassname.indexOf($(this).attr('class')) >= 0); //if it contains
}).show();
});
Well, I don't like working with index's in lists. I prefer that you retrieve it when you mount it on the server-side with attributes and id's.
So try the following:
$(document).ready(function () {
$('.personal_data p').on('click', function (event) {
var theTarget = $(this).attr('data-detail');
$('.person-details').removeClass('show'); //remove this line in case you don't want only one at a time
$('#' + theTarget).addClass('show');
});
})
.person-details {
opacity: 0;
}
.person-details.show {
opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="personal_data">
<p class="A" data-detail="ronnie"> Ronnie </p>
<p class="B" data-detail="james"> James </p>
<p class="C" data-detail="dio"> Dio </p>
</div>
<div class="persons">
<div>
<div>
<div class="A1 person-details" id="ronnie"> img1 </div>
</div>
</div>
<div>
<div>
<div class="B1 person-details" id="james"> img2 </div>
</div>
</div>
<div>
<div>
<div class="C1 person-details" id="dio"> img3 </div>
</div>
</div>
</div>
All the styling or class you can customize. If you prefer showing or hiding , just change the removeClass to hide, and the addClass to show
If you want the oposite interaction :
$(document).ready(function () {
$('.person-details').on('click', function (event) {
var theTarget = $(this).attr('data-detail');
$('.personal_data p').removeClass('show'); //remove this line in case you don't want only one at a time
$('#' + theTarget).addClass('show');
});
})
.personal_data p {
opacity: 0;
}
.personal_data p.show {
opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="personal_data">
<p class="A" id="ronnie"> Ronnie </p>
<p class="B" id="james"> James </p>
<p class="C" id="dio"> Dio </p>
</div>
<div class="persons">
<div>
<div>
<div class="A1 person-details" data-detail="ronnie"> img1 </div>
</div>
</div>
<div>
<div>
<div class="B1 person-details" data-detail="james" > img2 </div>
</div>
</div>
<div>
<div>
<div class="C1 person-details" data-detail="dio"> img3 </div>
</div>
</div>
</div>