I'm trying to target different elements on the page by it's data-product, and setting a rule that if the data-product is a certain number then to run the function. But at the moment the functions runs multiple times and i'm not sure why this is.
var targetProduct = document.querySelectorAll('.content[data-product]').forEach(function(el) {
if ('1'.indexOf(el.getAttribute('data-product')) > -1) {
addMessage();
} else if ('2'.indexOf(el.getAttribute('data-product')) > -1) {
addMessage();
}
});
function addMessage() {
document.querySelectorAll('.product').forEach(function(el) {
el.insertAdjacentHTML("afterend", "<p style='color: red'>Sale</p>");
});
}
<div class="content" data-product="1">
<p class='product'>Product 1</p>
</div>
<div class="content" data-product="2">
<p class='product'>Product 2</p>
</div>
<div class="content" data-product="3">
<p class='product'>Product 3</p>
</div>
<div class="content" data-product="4">
<p class='product'>Product 4</p>
</div>
Your addMessage() function is adding the Sale text to all elements through the .forEach method. You should instead pass to it the element that needs to have .insertAdjacentHTML done to it and then inside the function only update that one element.
Here is a working example. Note that I have combined the two if-statements into one using the logical OR (||) operator.
const targetProduct = document.querySelectorAll('.content[data-product]')
targetProduct.forEach(function(el) {
const attr = el.getAttribute('data-product')
if ('1'.indexOf(attr) > -1 || '2'.indexOf(attr) > -1) {
addMessage(el)
}
})
function addMessage(el) {
el.insertAdjacentHTML('afterend', "<p style='color: red'>Sale</p>")
}
<div class="content" data-product="1">
<p class="product">Product 1</p>
</div>
<div class="content" data-product="2">
<p class="product">Product 2</p>
</div>
<div class="content" data-product="3">
<p class="product">Product 3</p>
</div>
<div class="content" data-product="4">
<p class="product">Product 4</p>
</div>
If you want to add a label to particular item, you could use this. Does it solve your problem?
var targetProduct = document.querySelectorAll('.content[data-product]').forEach(function(el) {
if ('1'.indexOf(el.getAttribute('data-product')) > -1) {
addMessage('1');
} else if ('2'.indexOf(el.getAttribute('data-product')) > -1) {
addMessage('2');
}
});
function addMessage(id) {
document.querySelectorAll('.content[data-product="' + id + '"] .product').forEach(function(el) {
el.insertAdjacentHTML("afterend", "<p style='color: red'>Sale</p>");
});
}
<div class="content" data-product="1">
<p class='product'>Product 1</p>
</div>
<div class="content" data-product="2">
<p class='product'>Product 2</p>
</div>
<div class="content" data-product="3">
<p class='product'>Product 3</p>
</div>
<div class="content" data-product="4">
<p class='product'>Product 4</p>
</div>
Related
Whenever I click on the first button, I want to hide rest of elements and display the title with description for first item, I want to do this for the rest items as well. Whenever I am trying to do this, I am not able to target all elements in my function. Can someone assist? Also should I add to the buttons something like data-* and based on that display/show the div?
class MyClass {
constructor() {
this.items = document.querySelectorAll('.test');
this.btn = document.querySelector('.click');
console.log(this.items); //logs elements
this.btn.addEventListener("click", this.testFunc);
}
testFunc() {
console.log(this.items); //undefined ?
}
}
new MyClass();
.test {
display: none;
}
<div class="item first">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item second">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item third">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<button class="click">
Test
</button>
<button class="click">
Test
</button>
<button class="click">
Test
</button>
The following update to your MyClass should do the trick.
The main change is that you attach an event listenter to all buttons (changed querySelector('.click') to querySelectorAll('.click'). And the helper function showItem takes an index which is which item to show (and hide all the rest).
I'm using the hidden attribute to show/hide elements. You could instead add or remove a class from the item that has the display: none.
class MyClass {
constructor() {
this.items = document.querySelectorAll(".test");
this.btns = document.querySelectorAll(".click");
this.items.forEach((item, index) => {
item.setAttribute("hidden", true);
this.btns
.item(index)
.addEventListener("click", (ev) => this.showItem(index));
});
}
showItem(idx) {
this.items.forEach((item, index) => {
if (index === idx) {
item.removeAttribute("hidden");
} else {
item.setAttribute("hidden", true);
}
});
}
}
new MyClass();
<div class="item first">
<h3>Item-1</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item second">
<h3>Item-2</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<div class="item third">
<h3>Item-3</h3>
<p class="test">
dadwa dwao dawkda
</p>
</div>
<button class="click">
Test 1
</button>
<button class="click">
Test 2
</button>
<button class="click">
Test 3
</button>
I have a div with several paragraphs inside of it. I want each of these paragraphs to fade in one after the other. I can do that with the following code. However, since I will have many more divs with many other paragraphs, each with their unique class names, I wonder if there is an easier way to achieve this, without keep copy-pasting the code, changing the class names each time.
$('.line1').css('visibility','visible').hide().fadeIn(1000, function(){
$('.line2').css('visibility','visible').hide().fadeIn(1000, function(){
$('.line3').css('visibility','visible').hide().fadeIn(1000, function(){
$('.line4').css('visibility','visible').hide().fadeIn(1000);
});
});
});
.line1, .line2, .line3, .line4 {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line1">Text 01</p>
<p class="line2">Text 02</p>
<p class="line3">Text 03</p>
<p class="line4">Text 04</p>
</div>
Class names don't matter here, you can use .line or .line{i} or anything else, as long as there is an easy way to select all of them.
If you want different class names you could use .div1 > p in place of .line in the code.
If slight (+/- few milliseconds) innaccuracies aren't an issue, you could use setTimeout for this.
$(".line").each(function (i) {
$(this).css("opacity", 0);
setTimeout(() => {
$(this).animate({ opacity: 1 }, 1000);
}, 1000 * i);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line">Text 01</p>
<p class="line">Text 02</p>
<p class="line">Text 03</p>
<p class="line">Text 04</p>
</div>
You can also use .delay instead of setTimeout.
$(".line").each(function (i) {
$(this).css("opacity", 0).delay(1000 * i).animate({ opacity: 1 }, 1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line">Text 01</p>
<p class="line">Text 02</p>
<p class="line">Text 03</p>
<p class="line">Text 04</p>
</div>
You can use delay to delay subsequent fadeIns, like this:
for (var n = 1; n <= 4; ++n) {
$(".line" + n).css('visibility','visible').hide().delay(1000 * (n - 1)).fadeIn(1000);
}
Live Example:
for (var n = 1; n <= 4; ++n) {
$(".line" + n).css('visibility','visible').hide().delay(1000 * (n - 1)).fadeIn(1000);
}
.line1, .line2, .line3, .line4 {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line1">Text 01</p>
<p class="line2">Text 02</p>
<p class="line3">Text 03</p>
<p class="line4">Text 04</p>
</div>
Or with any list of class names:
["line1", "line2", "line3", "line4"].forEach(function(cls, index) {
$("." + cls).css('visibility','visible').hide().delay(1000 * index).fadeIn(1000);
});
Live Example:
["line1", "line2", "line3", "line4"].forEach(function(cls, index) {
$("." + cls).css('visibility','visible').hide().delay(1000 * index).fadeIn(1000);
});
.line1, .line2, .line3, .line4 {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div1">
<p class="line1">Text 01</p>
<p class="line2">Text 02</p>
<p class="line3">Text 03</p>
<p class="line4">Text 04</p>
</div>
Here is my HTML with 3 questions and 3 answers:
<div class="faq-carousel">
<div class="all-questions question1">
<h4>Question 1</h4>
</div>
<div class="all-questions question2">
<h4>Question 2</h4>
</div>
<div class="all-questions question3">
<h4>Question 3</h4>
</div>
<div class=" all-answers answer1">
<p>Answer 1</p>
</div>
<div class=" all-answers answer2">
<p>Answer 2</p>
</div>
<div class=" all-answers answer3">
<p>Answer 3</p>
</div>
Here is my jQuery that shows/hides the 3 questions and answers:
jQuery(document).ready(function () {
"use strict";
jQuery(".all-answers").hide();
jQuery(".answer1").show();
jQuery(".all-questions").removeClass("highlighted");
jQuery(".question1").addClass("highlighted");
var slideNumber = 1;
jQuery(".question1").click(function () {
jQuery(".all-answers").hide();
jQuery(".answer1").show();
jQuery(".all-questions").removeClass("highlighted");
jQuery(".question1").addClass("highlighted");
slideNumber = 1;
});
jQuery(".question2").click(function () {
jQuery(".all-answers").hide();
jQuery(".answer2").show();
jQuery(".all-questions").removeClass("highlighted");
jQuery(".question2").addClass("highlighted");
slideNumber = 2;
});
jQuery(".question3").click(function () {
jQuery(".all-answers").hide();
jQuery(".answer3").show();
jQuery(".all-questions").removeClass("highlighted");
jQuery(".question3").addClass("highlighted");
slideNumber = 3;
}); });
How can I change the jQuery so that I can add more Q and A's to the HMTL without having to add more jQuery?
Many thanks!
The process you're trying to achieve here is to 'DRY' up your code, in other words, Don't Repeat Yourself.
To achieve what you need you can use common classes on the questions and answers, then relate the two together by their indexes, something like this:
"use strict";
jQuery(document).ready(function($) {
$('.question').click(function() {
$('.question').removeClass('highlighted');
var index = $(this).addClass('highlighted').index();
$('.answer').hide().eq(index).show();
});
});
.answer { display: block; }
.answer ~ .answer { display: none; }
.highlighted { background-color: #CC0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="faq-carousel">
<div class="question">
<h4>Question 1</h4>
</div>
<div class="question">
<h4>Question 2</h4>
</div>
<div class="question">
<h4>Question 3</h4>
</div>
<div class="answer">
<p>Answer 1</p>
</div>
<div class="answer">
<p>Answer 2</p>
</div>
<div class="answer">
<p>Answer 3</p>
</div>
</div>
Alternatively, if you want to explicitly link the elements together, due to HTML structure restrictions for example, then you can use data attributes to specify the relationships between elements:
"use strict";
jQuery(document).ready(function($) {
$('.question').click(function() {
$('.question').removeClass('highlighted');
var target = $(this).addClass('highlighted').data('target');
$('.answer').hide().filter(target).show();
});
});
.answer { display: block; }
.answer ~ .answer { display: none; }
.highlighted { background-color: #CC0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="faq-carousel">
<div class="question" data-target="#answer-01">
<h4>Question 1</h4>
</div>
<div class="question" data-target="#answer-02">
<h4>Question 2</h4>
</div>
<div class="question" data-target="#answer-03">
<h4>Question 3</h4>
</div>
<div class="answer" id="answer-01">
<p>Answer 1</p>
</div>
<div class="answer" id="answer-02">
<p>Answer 2</p>
</div>
<div class="answer" id="answer-03">
<p>Answer 3</p>
</div>
</div>
Use a data attribute with the answer id.
Add the eventListener to all questions at once using jQuery(".all-questions").click
use jQuery('.answer'+jQuery(this).data('answer')).show(); to show current answer.
this will use current element.
use jQuery(this).addClass("highlighted"); to add the class to current element
To add the slide number, use slideNumber = jQuery(this).data('answer');
jQuery(document).ready(function() {
"use strict";
jQuery(".all-answers").hide();
jQuery(".answer1").show();
jQuery(".all-questions").removeClass("highlighted");
jQuery(".question1").addClass("highlighted");
var slideNumber = 1;
jQuery(".all-questions").click(function() {
jQuery(".all-answers").hide();
jQuery('.answer'+jQuery(this).data('answer')).show();
jQuery(".all-questions").removeClass("highlighted");
jQuery(this).addClass("highlighted");
slideNumber = jQuery(this).data('answer');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="faq-carousel">
<div data-answer="1" class="all-questions question1">
<h4>Question 1</h4>
</div>
<div data-answer="2" class="all-questions question2">
<h4>Question 2</h4>
</div>
<div data-answer="3" class="all-questions question3">
<h4>Question 3</h4>
</div>
<div class=" all-answers answer1">
<p>Answer 1</p>
</div>
<div class=" all-answers answer2">
<p>Answer 2</p>
</div>
<div class=" all-answers answer3">
<p>Answer 3</p>
</div>
Give all questions and answer elements a data-number attribute with the correct number then use
$(".all-questions").click(function() {
$(".all-questions").hide();
var slideNumber = $(this).data("number");
$(".answer"+slideNumber).show();
$(".all-questions").removeClass("highlighted");
$(this).addClass("highlighted");
}
With no changes to the HTML, something like this should do it :
jQuery(function ($) {
"use strict";
$('.all-questions').on('click', function() {
$('.all-answers').hide().filter('.answer' + $(this).index()).show();
$('.all-questions').removeClass('highlighted').filter(this).addClass('highlighted');
});
$(".question1").trigger('click');
});
Try this one. Very simple. The trick I used is to associated every question class name with it's answers class name. For instance, if Question 1 class name is question1, its answer class name is question1_answer. After that let the magic happen (you can add your style however you want. Just copy/paste and run that code an see what it does.
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script>
function myFunction(classname){
var answerClassName = "." + classname + '_answer';
$(answerClassName).show();
//Hide all other answers
var otherAnswers = document.body.getElementsByTagName("div");
var l = otherAnswers.length;
for(i=0 ; i<l ;i++){
if(otherAnswers[i].className == classname){
//do nothing
}else{
var otherAnswersClassName = "." + otherAnswers[i].className + '_answer';
jQuery(otherAnswersClassName).hide();
}
}
}
</script>
</head>
<body>
<div class="question1" onclick="myFunction(this.className)">
<h4>Question 1</h4>
</div>
<div class="question1_answer">
<p>Answer 1</p>
</div>
<div class="question2" onclick="myFunction(this.className)">
<h4>Question 2</h4>
</div>
<div class="question2_answer">
<p>Answer 2</p>
</div>
</body>
</html>
I have a page with 'previous' and 'next' buttons to traverse an array of divs.
Clicking next button will copy the next div indexed by the var 'click' into #place's innerHTML.
Clicking previous button will copy the previous div as you can imagine.
The problem is, when I click the button alternately(click previous and next and previous then next...), the div would remain as it is and will not change to the next or previous div, unless I click the same button twice.
A simple alert message would confirm the functions are executing when alternately clicked. What's going on? Thank you.
var clicks = 0;
var pageDivs = document.getElementsByClassName("plist");
var displayPanel = document.getElementById("place");
function g() {
if (clicks >= pageDivs.length) {
clicks = 0;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
clicks += 1;
}
function f() {
clicks -= 1;
if (clicks < 0) {
clicks = pageDivs.length - 1;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button type="button" onclick="f()" style="width: 10%;">Previous</button>
<button type="button" onclick="g()" style="width: 10%;">Next</button>
You need to consistently add and subtract in the same place.
But no need for two functions
NOTE: for an additional improvement, just hide and show instead of inserting code into the DOM
var clicks = -1, // necessary for initial next clicking
pageDivs = document.getElementsByClassName("plist"),
displayPanel = document.getElementById("place"),
len = pageDivs.length;
function copyIt(direction) {
clicks += direction;
if (clicks >= len) clicks = 0;
else if (clicks < 0) clicks = len - 1;
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button type="button" onclick="copyIt(-1)" style="width: 10%;">Previous</button>
<button type="button" onclick="copyIt(1)" style="width: 10%;">Next</button>
When you click Next, increment your counter first. You're doing it after, which is causing the issue.
Define your function f like this:
function g() {
clicks += 1; // increment at the start
...
}
var clicks = 0;
var pageDivs = document.getElementsByClassName("plist");
var displayPanel = document.getElementById("place");
function g() {
clicks += 1;
if (clicks >= pageDivs.length) {
clicks = 0;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
function f() {
clicks -= 1;
if (clicks < 0) {
clicks = pageDivs.length - 1;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button type="button" onclick="f()" style="width: 10%;">Previous</button>
<button type="button" onclick="g()" style="width: 10%;">Next</button>
The problem was clicks counter was getting updated after updating the innerHTML.
Moving that to top fixed the issue.
$(document).ready(function() {
var clicks = -1;
var pageDivs = document.getElementsByClassName("plist");
var displayPanel = document.getElementById("place");
$("#prev").on("click", f);
$("#next").on("click", g);
function g() {
clicks += 1;
if (clicks >= pageDivs.length) {
clicks = 0;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
function f() {
clicks -= 1;
if (clicks < 0) {
clicks = pageDivs.length - 1;
}
displayPanel.innerHTML = pageDivs[clicks].innerHTML;
}
});
.plist {
display: none;
}
#place {
background-color: lightskyblue;
min-height: 5%;
min-width: 10%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="place">
<p></p>
</div>
<div class="plist">
<p>Testing 1</p>
</div>
<div class="plist">
<p>Testing 2</p>
</div>
<div class="plist">
<p>Testing 3</p>
</div>
<div class="plist">
<p>Testing 4</p>
</div>
<div class="plist">
<p>Testing 5</p>
</div>
<button id="prev" type="button" style="width: 10%;">Previous</button>
<button id="next" type="button" style="width: 10%;">Next</button>
I've got a little issue with JavaScript. I am not sure if my code not work, or if I am about lunch it wrong way.
<title>Untitled Document</title>
<script language="javascript">
function Unhide()
{
var item = document.getElementsByTagName('p');
for (x; x > item.length; x+1)
{
if (item.item(x).getAttribute('hidden') == ('true'))
{
item.item(x).setAttribute('hidden', 'false');
}
else
{
item.item(x).setAttribute('hidden', 'true');
}
}
}
</script>
</head>
<body>
<div class="Level1">
<p class="Menu">Home</p>
<div class="Level2">
<p class="Menu">Artykół 1</p>
<p class="Menu">Artykół 2</p>
<p class="Menu">Artykół 3</p>
<p class="Menu">Artykóły Autorskie</p>
</div>
<p class="Menu">Dziennik</p>
<p class="Menu">Archiwum</p>
<div class="Level2">
<p class="Menu"><a onclick="Unhide()">Ostatni Tydzień</a></p>
<div class="Level3">
<p class="Menu" hidden="true">Art1</p>
<p class="Menu" hidden="true">Art2</p>
</div>
</div>
</div>
</body>
Could you give me any tip, how to figure it out?
function Unhide()
{
var items = document.getElementsByTagName('p');
for (var x in items)
{
if (x.style.display == 'none')
{
x.style.display = '';
}
else
{
x.style.display = 'none';
}
}
}
items is an array of elements. iterate through them with a for loop and set style.display property.
Reference: http://www.w3schools.com/jsref/prop_style_display.asp
http://davidwalsh.name/html5-hidden
<script>
function Unhide()
{
var items = document.getElementsByTagName('p');
for ( var x = 0; x < items.length; x++)
{
var item = items[x];
if (item.getAttribute('hidden') == 'true')
{
item.setAttribute('hidden', 'false');
}
else
{
item.setAttribute('hidden', 'true');
}
}
}
</script>
<div class="Level1">
<p class="Menu">Home</p>
<div class="Level2">
<p class="Menu">Artykół 1</p>
<p class="Menu">Artykół 2</p>
<p class="Menu">Artykół 3</p>
<p class="Menu">Artykóły Autorskie</p>
</div>
<p class="Menu">Dziennik</p>
<p class="Menu">Archiwum</p>
<div class="Level2">
<p class="Menu"><a onclick="Unhide()">Ostatni Tydzień</a></p>
<div class="Level3">
<p class="Menu" hidden="true">Art1</p>
<p class="Menu" hidden="true">Art2</p>
</div>
</div>
</div>
For me works:
item.attr('hidden', false);
but only if the item's attribute looks like:
hidden="true"
not only "hidden".