display data attribute element of the current slider - javascript

I'm a beginner in JS, I made a carousel with a data attribute and I want to display it and it have to change when we go the next or previous slider.
For the moment I made a small script and I can display only the data attribute of the first slider
HTML :
<div class="carousel2partsUp slick-initialized slick-slider">
<div class="el-carousel2parts slick-slide slick-current slick-active" data-legend="legend1">
{# Content #}
</div>
<div class="el-carousel2parts slick-slide" data-legend="legend2">{# Content #}</div>
<div class="el-carousel2parts slick-slide" data-legend="legend3">{# Content #}</div>
<div class="el-carousel2parts slick-slide" data-legend="legend4">{# Content #}</div>
</div>
<span id="legend"></span>
JS :
<script>
var carousel = document.querySelector('.el-carousel2parts');
var spanLegend = document.querySelector('#legend');
var dataLegend = carousel.dataset.legend;
spanLegend.innerHTML = dataLegend;
</script>
How can I improve my code to display the data attribute according to the good slider ?
should I make a loop ? or something with a event click ?
Thanks for answer

If you have only one active slide you can do it without loop:
var carousel = document.querySelector('.el-carousel2parts');
var spanLegend = document.querySelector('#legend');
// Find which is active and get its attribute:
var currentSlide = document.querySelector('.slick-active');
var currentData = currentSlide.getAttribute('data-legend');
var dataLegend = currentData;
spanLegend.innerHTML = dataLegend;
I do not see you full code, so just a guess.You can make onClick function and call new two lines of code. Put onClick on button which updates the slides. Imagine we have several buttons that scroll through the slides.
<button class="turn">Left</button>
<button class="turn">Right</button>
In that case we can have something like this:
var spanLegend = document.querySelector('#legend');
var btnsTurn = document.querySelectorAll('.turn');
btnsTurn.forEach(item => {
item.addEventListener("click", () => {
var currentSlide = document.querySelector('.slick-active');
var currentData = currentSlide.getAttribute('data-legend');
spanLegend.innerHTML = currentData;
});
})
But I assume that you already have a function which add Class 'slick-active' and remove it from another slide. So you can just add some code inside it without new forEach for the same buttons.
PS You take an Attribute from your DOM which can be edited by any user. In general, be careful innerHTML is a dangerous function.
Hope this helps! Regards,

Related

Using an attribute value to add custom links for each images in my wordpres gallery

I'm using Elementor Pro for my photography website and i want to set custom links for each image in the gallery. By default, i can only set a global link which is the same for each image so that's not great.
In fact, i want to set a custom link which will redirect the user to the shop page of the specific image.
I found some code online (thanks to elementhow !) and it's really close to what i want, but i still need to change some things. The thing is a have to manually write all the links in an array and it's not convenient (close to 100 files and growing, if i change the order, i have to reorder the links, etc).
Here's the code i currently use :
<style>.e-gallery-item{cursor: pointer;} </style>
<script>
document.addEventListener('DOMContentLoaded', function () {
var filteredImages = document.querySelectorAll('.e-gallery-item');
//Edit the links HERE
var links = [
'https://www.mywebsiteurl.com/product/product-name1/',
'https://www.mywebsiteurl.com/product/product-name2/',
'https://www.mywebsiteurl.com/product/product-name3/',
'https://www.mywebsiteurl.com/product/product-name4/',
];
var _loope = function _loope(i) {
filteredImages[i].addEventListener('click', function () {
location = links[i];
});
};
for (var i = 0; i < filteredImages.length; i++) {
_loope(i);
}
});
</script>
I would like to use an attribute value in the algorithm to generate the link automatically for each image. I have the process in my mind, but i don't know how to code this...
Here's the code of one image ,i can set what value i want in "alt".
<div class="e-gallery-image elementor-gallery-item__image e-gallery-image-loaded" data-thumbnail="......" data-width="1024" data-height="768" alt="product-name";"></div>
I would like to use the "alt" attribute to create a unique url for each file, with this format :
'https://www......com/product/product-name/'
"product-name' will take the value of the "alt" attribute of each image.
I tried to change this part of the code (replace "links[i]" by trying to get the attribute value using filteredImages[i].getAttributes) but without success...
var _loope = function _loope(i) {
filteredImages[i].addEventListener('click', function () {
location = links[i];
});
};
Can someone give me some tips about how to do that ? I spend 2 years without coding so i'm a bit rusty...
I think this does what you'd like it to, as you have already done you can cycle through each image link using a class.
You can get the value of alt using the .attr() function and then replace the href value of the link.
DEMO
// Cycle through each image using common class
$(".e-gallery-image").each( function() {
// Get value of 'alt' for clicked item
alt = $(this).attr("alt");
// Update href value
$(this).attr("href", "https://www.mywebsiteurl.com/product/" + alt );
});
// Prove that its worked
$(".e-gallery-image").click( function() {
// Confirm all is correct
console.log($(this).attr("href"));
// Assign url to window
location.href = $(this).attr("href");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="e-gallery-image" href="test.com" alt="product1">Product 1</div>
<div class="e-gallery-image" href="test.com" alt="product2">Product 2</div>
<div class="e-gallery-image" href="test.com" alt="product3">Product 3</div>
<div class="e-gallery-image" href="test.com" alt="product4">Product 4</div>
<div class="e-gallery-image" href="test.com" alt="product5">Product 5</div>
In fact it was really simple. I tried to target a parent element and it worked ! If anyone is interested, here's the code i use :
document.addEventListener('DOMContentLoaded', function () {
var filteredImages = document.querySelectorAll('YOUR_SELECTOR');
var _loope = function _loope(i) {
filteredImages[i].addEventListener('click', function() {
location = "YOUR_WEBSITE_URL" + filteredImages[i].querySelector("YOUR_SELECTOR").getAttribute("alt");
});
};
for (var i = 0; i < filteredImages.length; i++) {
_loope(i);
}
});

Is there any way to know if a certain button has been pressed in a certain location inside a div element in JavaScript?

Basically I'm trying to make a todo list app similar to Trello. I have a button that when pressed turns into an input element, gets a "To Do Task" item and adds that to a list. This is achieved by this piece of code:
function createCardBoxNode(title){
/*HTML looks like:
<div class="task-card">
<div class="writings">
<p class="title">Tasks To Do</p>
<ul id="tasks">
<li>Task 1</li>
</ul>
</div>
<button class="btn-add-task">
Add New Task...
</button>
<input....>
</div>
*/
var containerBox = createElement('div', {class:'task-card'});
var writingPartBox = createWritingAreaNode(title);
var newTaskBtn = createElement('button', {class:'btn-add-task show'},'Add New Task...');
var newTaskInput = createElement('input', {class:'new-task hide', type:'text', placeholder:'New Task'});
//When 'add new task' is clicked, make it an input area
newTaskBtn.addEventListener('click', function(){
newTaskBtn.classList.remove('show');
newTaskBtn.classList.add('hide');
newTaskInput.classList.remove('hide');
newTaskInput.classList.add('show');
newTaskInput.focus();
});
// when input is entered, that's a new "To Do Task" so add it to the list
newTaskInput.addEventListener('keyup', function(e){
if (e.keyCode === 13){
// If Enter is pressed
var newTask = createListItems(newTaskInput.value);
var listArea = document.getElementById('tasks');
listArea.appendChild(newTask);
newTaskInput.classList.remove('show');
newTaskInput.classList.add('hide');
newTaskInput.value = '';
newTaskBtn.classList.remove('hide');
newTaskBtn.classList.add('show');
}
});
containerBox.appendChild(writingPartBox);
containerBox.appendChild(newTaskBtn);
containerBox.appendChild(newTaskInput);
return containerBox;
}
This works fine until I add another Card at the same time and decide to add new tasks to the second card. Then every task gets added to the first card. I wonder if there is any way to check if the "input" that's being sent is going to a specific card checking the card's title. I don't have any limits on how many tasks can be added to each card, and don't want to add that. I also want the user to be able to work on two separate cards at the same time. As a beginner, I also want to fix this using only JavaScript. I hope I've explained the issue well enough.
Edit:
I have tried doing this:
if (document.querySelector('.title').innerText === title){
var newTask = createListItems(newTaskInput.value);
var listArea = document.getElementById('tasks');
listArea.appendChild(newTask);
newTaskInput.classList.remove('show');
newTaskInput.classList.add('hide');
newTaskInput.value = '';
newTaskBtn.classList.remove('hide');
newTaskBtn.classList.add('show');
But then I cannot add anything new to the second box.
I think your main problem is that you use the <ul> with the same id for different cards.
First of all, change your markup and replace <ul id="tasks"> with <ul class="tasks-list">
<div class="task-card">
<div class="writings">
<p class="title">Tasks To Do</p>
<ul class="tasks-list"> <!-- !!! here !!! -->
<li>Task 1</li>
</ul>
</div>
<button class="btn-add-task">
Add New Task...
</button>
<input....>
</div>
and then change that selector in your keyup handler:
//...
var listArea = containerBox.querySelector('.tasks-list');
// ...
Also, it would be better to declare
var listArea = containerBox.querySelector('.tasks-list');;
outside your handler.
I solved it on my own.
The trick is to not select anything at all. Everytime you queryselect anything, or get an element using it's ID - it will select the first element with that id or class.
What I ended up doing is to combine two of my functions, have a function generate my and then just straight up append the list items into that ul. No selection whatsoever.

How to dynamically change info modal innerHTML for each clicked element from an array

I am trying to make a small collection of recipes and four of them are already stored inside an array of objects, each object representing another recipe. My problem is that I want to make an info window, a modal if you will, which will show information about each clicked recipe that's stored inside its object.
The thing is whenever i try to set innerHTML of said modal the for loop I created shows entire object and so far I didn't find out how to make each click on modal show only the info for one recipe. (First link should show the details for the first recipe, second for the second and so on).
I tried a for loop which should dynamically loop content for the info window depending on the clicked element but it shows the entire object and so far I'm not sure what other method would be a better solution.
My array of objects looks like this
var recipes = [
{
key: 0,
title: 'Pasta Carbonara',
ingredients: 'etc',
instructions: 'etc'
},
{
key: 1,
title: 'etc',
ingredients: 'etc',
instructions: 'etc'
},
and so on (4 objects)
]
and my for loop looks like this:
function openModal() {
detailsModal.style.display = 'block';
var modalContent = document.getElementById('modalInfo');
var modalBody = '';
for (var i=0; i < recipes.length; i++){
modalBody += JSON.stringify(recipes[i])
}
modalContent.innerHTML = modalBody;
}
The entire code's here: https://codepen.io/Ellie555/pen/KOwexB
This question is really mundane but if you had any suggestions I'd appreciate it.
One way would be to add data attributes to the anchors:
Pasta Carbonara
And then using those attributes to instruct your modal code which recipe to load:
function openModal(e) {
detailsModal.style.display = 'block';
var modalContent = document.getElementById('modalInfo');
// The critical line:
var modalBody = JSON.stringify(recipes[parseInt(e.currentTarget.dataset.recipeIndex)]);
modalContent.innerHTML = modalBody;
}
Full code: https://codepen.io/mac9416/pen/BXyPdO
Aside: I would use <button> elements styled as links instead of anchors for accessibility.
Your markup above isn't semantic html since you're not redirect or navigating. So first of all I'd replace ... tag with <button type="button">...</button>:
<div class="main">
<div class="recipes" id="recipeSection">
<div class="recipe-entry">
<div class="name"><button type="button" id="0">...</button></div>
</div>
<div class="recipe-entry">
<div class="name"><button type="button" id="1">...</button></div>
</div>
<div class="recipe-entry">
<div class="name"><button type="button" id="2">...</button></div>
</div>
<div class="recipe-entry">
<div class="name"><button type="button" id="3">...</button></div>
</div>
</div>
</div>
To answer your question to dynamically change info modal innerHTML for each clicked element from an array!
add id to each element that will be clicked to associate it with the desired object in your array
filter that array based on the click target with its id
const data = recipes.filter(recipe => recipe.key === Number(event.target.id));
modalContent.innerHTML = JSON.stringify(data[0]);
I forked and modified your code. Here's a working Demo.
Note:
If you're not sure about key values in your array for each item (i.e. dynamically) you can iterate over it and append it into your DOM.
You can also create list from Javascript instead statically create it in HTML.
My code with comments here:
https://stackblitz.com/edit/js-bu6tz8?file=index.js

jquery show() not displaying properly for highcharts graph

So i'm trying to display 3 graphs in the same div, toggled by buttons which show/hide the other divs respectively. I've set the other 2 graphs to
style= "display: none"
To ensure only one graph is shown upon load. This is how the default view looks like:
The default view is the day on day button. However, when I click the other 2 buttons, the width of the graph screws up, and it displays like this.
It shrinks for some reason. I have switched the order of display, and it's always the hidden graphs which have the size problem. I suspect it has something to do with the inline style property, but I cant figure out how to make it show properly.
Code snippet for graph:
<button onclick="showDay('tasks')">Day on Day</button>
<button onclick="showWeek('tasks')">Week on Week</button>
<button onclick="showMonth('tasks')">Month on Month</button>
<div class="portlet-body">
<div id="tasks"></div>
<div id="tasksWeek" style="display: none"></div>
<div id="tasksMonth" style="display: none"></div>
</div>
<script>
new Highcharts.StockChart({{masterDic['tasks']|safe}});
new Highcharts.StockChart({{masterDic['tasksWeek']|safe}});
new Highcharts.StockChart({{masterDic['tasksMonth']|safe}});
</script>
code snippet for calling (hackish right now)
<script>
function showDay(id) {
var idDay = "#"+id;
var idWeek = "#"+id+"Week";
var idMonth = "#"+id + "Month";
$(idWeek).hide(10);
$(idMonth).hide(10);
$(idDay).show(10);
}
function showWeek(id) {
var idDay = "#"+id;
var idWeek = "#"+id+"Week";
var idMonth = "#"+id + "Month";
$(idMonth).hide(10);
$(idDay).hide(10);
$(idWeek).show(10);
}
function showMonth(id) {
var idDay = "#"+id;
var idWeek = "#"+id+"Week";
var idMonth = "#"+id + "Month";
$(idDay).hide(10);
$(idWeek).hide(10);
$(idMonth).show(10);
}
</script>
Anyone have any ideas on how to fix this? Thanks alot! :)
EDIT:
css for portlet body (entire trace when using inspect element):
https://jsfiddle.net/ovnrpnb5/
setTimeout(function(){
$(window).resize();
})
Call this after show()
Figured it out if anyone's interested. Tldr: call reflow() on the chart after showing.
I was using hide() and show() instead of tabs as per #Grzegorz BlachliƄski's comment, so the solution given wasn't working.
I found this link which showed you how to access the element within your HTML http://ahumbleopinion.com/highcharts-tips-accessing-chart-object-from-container-id/
I was using the highcharts CDN, which apparently isnt 3.0.1, so the jquery method wasnt working. Hence, i made the following function and called it after every show()
// HACK TO GET IT TO DISPLAY PROPERLY
function callReflow(id){
var index = $(id).data('highchartsChart');
var chart = Highcharts.charts[index];
chart.reflow();
}
Worked like a charm :)

Do I need to create multiple functions for multiple actions or can they all be housed in the same function?

I'm working on a script to simulate a page change in a Questionnaire I'm building. I figured maybe I could use a bunch of "if" statements to house all the logic but it's not working right, before I go and create separate functions I'd like to know if it's possible to put them all in one single function.
So far this is the script
function pageChange(){
var chng1 = document.getElementById("p1next");
var chng2a = document.getElementById("p2back");
var chng2b = document.getElementById("p2next");
var chng3a = document.getElementById("p3back");
var chng3b = document.getElementById("p3next");
var pg1 = document.getElementById("page01");
var pg2 = document.getElementById("page02");
var pg3 = document.getElementById("page03");
if (chng1.click){
pg1.style.display="none";
pg2.style.display="block";
}
if (chng2a.click){
pg1.style.display="block";
pg2.style.display="none";
}
the "p1next, p2back, p2next etc." are IDs I gave the buttons on the pages, which I have in DIVs that I respectively named "page01, page02, page03 etc."
Without the 2nd if statement the script works exactly how I want it, it changes the display for "page01" to none and the div for "page02" to block. When I add the second if statement it doesn't work.
The reason I want to do it like this rather than making actual pages is because I don't want the data to get lost when they load another page. Am I on the right track or do I need to create a new function for each page?
Not exactly on the right track, you should use onclick events, instead of if (x.click) like this:
var chng1 = document.getElementById("p1next");
var pg1 = document.getElementById("page01");
var pg2 = document.getElementById("page02");
// Events
chng1.onclick = function(){
pg1.style.display="none";
pg2.style.display="block";
};
This will save your function until the element is clicked and then execute that function. In your case, it is executed on page load, and at that moment the user is not clicking anything.
Why not try something like this:
HTML:
<div class="page" data-pg="1">...</div>
<div class="page" data-pg="2">...</div>
<div class="page" data-pg="3">...</div>
<input id="btnPrev" type="button" value="Prev" />
<input id="btnNext" type="button" value="Next" />
jQuery:
var pageNum = 1;
$(document).ready(function () {
$("#btnPrev").on("click", function () { ChangePage(-1); });
$("#btnNext").on("click", function () { ChangePage(1); });
ChangePage(0);
});
function ChangePage(p) {
$(".page").hide();
pageNum += p;
$(".page[data-pg='" + p + "']").show();
$("#btnPrev").removeAttr("disabled");
$("#btnNext").removeAttr("disabled");
if (pageNum === 1) $("#btnPrev").attr("disabled", "disabled");
if (pageNum === $(".page").length) $("#btnNext").attr("disabled", "disabled");
}
That way you can easily grow your number of pages without changing the script. My apologies by the way for doing this in jQuery.
Update:
Have a lot of time on my hands today and have not coded for while using vanilla Javascript. Here's the version of the code using plain js: https://jsfiddle.net/hhnbz9p2/

Categories

Resources