Iterate through an array on click and change albums via another click - javascript

I have a ngFor loop which iterates over an array of famous people (see below), this is fine but I would now like to add in image gallery which is where things get tricky. Each person will have 2 photo albums, the albums will be simply strings of urls to remote photos - the first problem is that I would like to be able to click the image tag (or element close by) and on each click I want the image src to change to the next image, when it reaches the end ideally I would like it to go back again to the start or just stop.
this is tricky enough - but each person will have a secondary photo album which is much the same as the first i.e Array however - it gets activated when the user clicks the changeAlbum() which is on a thumbnail image below - the idea is that the first image of the array not in use (as clicking changeAlbum() makes it inactive ) populates this thumbnail and the gallery then uses the images from the secondary gallery - so essentially its like a complex toggle() between the 2 galleries combined with a click to iterate over the array of each gallery - it might even be cool if when you iterate through the first gallery that it auto switches to the secondary one and vice versa
Im not entirely sure how to approach it though - the html and the json struct are the only solid things I have in place.
<div class="card" *ngFor="let c of item" >
<div class="card-content">
<div class="card-image">
<img (click)="changePhoto()" [src]="{{ output image here from the click }}" />
</div>
<div class="card-titles">
<h1>
{{ c.name }}
</h1>
</div>
<div class="card-image">
<img (click)="changeAlbum()" [src]="{{ show image from album not in use here}}" />
</div>
</div>
</div>
The data structure I have so far is like this
item = [{
id: 1,
name: "Joe Jimbob",
album_Main: [{
selected: true,
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
}],
album_Secondary: [{
selected: false,
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
"https://via.placeholder.com/250",
}]
}]
This is also relatively solid but im very open to any help anyone can give
changeAlbum(item:any) :void {
if( item.album_Main.selected ) {
item.album_Main.selected == false
} else {
item.album_Secondary.selected == false
}
}
and the change...
changePhoto(photos: Array<string>) :void {
// not sure how to interate this one by one on click
console.log( photos.length )
}

Check this Stackblitz example I made based on your code: https://stackblitz.com/edit/angular-pk6cu8
The important changes are these in app.component.ts
photoIndex = 0;
personIndex = 0;
selectedAlbum = this.item[0].album_Main;
altAlbum = this.item[0].album_Secondary;
isMainAlbumActive = true;
changePhoto() {
if (this.photoIndex < this.selectedAlbum.length - 1) {
this.photoIndex++;
} else {
this.photoIndex = 0;
}
}
changeAlbum() {
if (this.isMainAlbumActive) {
this.selectedAlbum = this.item[this.personIndex].album_Secondary;
this.altAlbum = this.item[this.personIndex].album_Main;
} else {
this.selectedAlbum = this.item[this.personIndex].album_Main;
this.altAlbum = this.item[this.personIndex].album_Secondary;
}
this.isMainAlbumActive = !this.isMainAlbumActive;
}

Related

How click a link on one page and have a specific filter option chosen on linked page?

I've been searching all over the internet for this one, and either the suggestions don't seem to apply, or I'm not using the right terminology to describe what I'm wanting to do, so nothing comes up.
Here's the long story:
On our Home Page, we have a section called "Industries". In this section, there are a few choices, "Government", "Manufacturing", "Energy", "Health Care", etc. A visitor to our site can click on one of those choices and it will take them to our Product Filter Page. I would like to have it so that if they clicked on "Manufacturing", that the option would already be chosen on the Product Filter Page, so they only see our products that are available for the Manufacturing Industry.
I am not a coder by any means, but I have been able to take code that I find on forums, and slightly modify it to make it work on our site, to achieve other functionality that we've wanted. But I can't seem to find anything that makes any sense to me that would allow me to modify it to work for us in this case.
Here is the code that makes up the drop-down list on the Product Filter Page, that relates to the Industries that could be chosen:
<div class="dv-dropdown">
<div class="caption">All Industries</div>
<div class="list">
<div class="item df-button">All Industries</div>
<div class="item df-button dfc-retail">Retail</div>
<div class="item df-button dfc-construction">Construction</div>
<div class="item df-button dfc-warehousing">Warehousing</div>
<div class="item df-button dfc-manufacturing">Manufacturing</div>
<div class="item df-button dfc-government">Government</div>
<div class="item df-button dfc-energy">Energy</div>
<div class="item df-button dfc-automotive">Automotive</div>
<div class="item df-button dfc-printing">Printing</div>
<div class="item df-button dfc-healthcare">Health Care</div>
</div>
</div>
I'm not sure what exactly I could include for the Javascript that is on the page, as it is from a purchasable plugin - Divi Filter - and I'm pretty sure that this will need custom script to get it to work.
This is my first post, so please let me know if you need anything else from me. I am currently in the process of creating the site, so it's not exactly live. Thus, if there's any reference to URL links, please just make use of a generic site example, and I can replace it as needed.
Thank you all in advance for your help, and I will do my best to respond to any questions you may have, but please keep in mind my inexperience with coding.
EDIT:
Here is a snippet of the Javascript used for filtering the classes:
var activeClasses = [];
jQuery(function() {
jQuery('.dv-dropdown .df-button').on('click', function() {
// get number of dropdown
var dvIndex = jQuery(this).closest(".dv-dropdown").index(".dv-dropdown");
/* remove class */
if (activeClasses[dvIndex] != "") {
jQuery('.dv-dropdown:not(:eq(' + dvIndex + ')) .df-button').each(function() {
jQuery(this).removeClass(activeClasses[dvIndex]);
});
activeClasses[dvIndex] = "";
}
// get button classes
var filterClasses = jQuery(this).attr('class').split(/\s+/);
// remove all classes except dfc-
filterClasses = jQuery.grep(filterClasses, function(element) {
return element.indexOf("dfc-") === 0;
});
// remove all other active classes from button
jQuery.each(activeClasses, function( index, value ) {
if(index !== dvIndex) {
filterClasses = filterClasses.filter(e => e !== activeClasses[index]);
}
});
if (filterClasses[0] != undefined) { // undefined if you click on all, because no class then just remove
activeClasses[dvIndex] = filterClasses[0];
jQuery('.dv-dropdown:not(:eq(' + dvIndex + ')) .df-button').each(function() {
jQuery(this).addClass(activeClasses[dvIndex]);
});
}
// add active button class dv-activebutton
jQuery.each(activeClasses, function( index, value ) {
// remove classes
jQuery(".dv-dropdown:eq(" + index + ") .df-button").removeClass("dv-activebutton");
// add it to active element
if(value !== "" && typeof value !== 'undefined') {
jQuery(".dv-dropdown:eq(" + index + ") .df-button." + value).addClass("dv-activebutton");
}
else {
jQuery(".dv-dropdown:eq(" + index + ") .df-button.dv-all").addClass("dv-activebutton");
Here is a snippet of the Javascript used for the dropdowns
jQuery(function() {
/* toggle open class */
jQuery('.dv-dropdown > .caption').on('click', function() {
jQuery(this).parent().toggleClass('open');
jQuery('.dv-dropdown > .caption').not(this).parent().removeClass('open');
});
/* make item active element and add to caption */
jQuery('.dv-dropdown > .list > .item').on('click', function() {
jQuery(this).siblings().removeClass('selected');
jQuery(this).addClass('selected').parent().parent().removeClass('open').children('.caption').text( jQuery(this).text() );
});
/* close dropdown if Esc is clicked on keyboard */
jQuery(document).on('keyup', function(evt) {
if ( (evt.keyCode || evt.which) === 27 ) {
jQuery('.dv-dropdown').removeClass('open');
}
});
/* on click remove, close dropdown */
jQuery(document).on('click', function(evt) {
if ( jQuery(evt.target).closest(".dv-dropdown > .caption").length === 0 ) {
jQuery('.dv-dropdown').removeClass('open');
The way I would do this is add a query string to the URL that the link takes you to. So if you clicked "Manufacturing" on the first page, it would append something like "?industry=Manufacturing" to the end of the URL for the product filter page. Then, on the product filter page you would write a javascript function get the value of the query param (window.location.search). This query param would be the "value" chosen for the dropdown.
Are you using any javascript frameworks? A snippet of the code you have for the product filter page would be helpful.

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

Can't make multiple inners draggable object from angular 5 & dragula

I'm trying since few days and can't make it works...
Little explanation :
I've in this example, an array of object like this :
public containers: Array<object> = [
{
"name": "container 1",
"items": ["1", "2", "3"]
},
{
"name": "container 2",
"items": ["4", "5"]
}
];
Where each object have an array of string.
I'm generating divs to make these object moves (Containers AND Items).
Now, i'm getting something like this :
Where red box is the main div, blacks boxes are containers, and blue boxes are items.
with this html :
<div class="feuille-drag" [dragula]='"containers"' [dragulaModel]='containers'>
<div *ngFor="let container of containers" class="drag-container" [dragula]='"blocks"'[dragulaModel]='container.items'>
<span class="handle">O</span>
<div *ngFor="let item of container.items" class="drag-bloc">
<span class="handleF">X</span>
{{item}}
</div>
</div>
And this typescript, where I only set fews options :
dragulaService.setOptions('containers', {
revertOnSpill: true,
copy: false,
moves: function (el, container, cHandle) {
return cHandle.className === 'handle';
}
});
dragulaService.setOptions('blocks', {
revertOnSpill: true,
copy: false,
moves: function (el, container, bHandle) {
return bHandle.className == 'handleF';
}
});
If you looks well, you can see in the screenshot that, there is an empty blue box. It wasn't here at the beginning, I only dropped a blue box into another one, and it created an undefined element into my object Containers.
An other problem :
If I move a blue box into an other black box (container), the blue box will disappear and an other blue box will move instead.
Example : If I move the blue box 1 into the container 2, the box 1 will disappears, and the box 2 will go into the container 2.
BUT It will not be deleted into the object Containers :
End, last thing, handle elements from containers (O) are being read like draggable object from dragula.
Its maybe just a css problem, but i'm not sure so...
I'm using Angular CLI, Angular 5, Dragula, and i'm pretty new on Angular, (I still was on AngularJS sometimes ago).
I hope it's well explained, hope someone can help me, and I'm sorry if there is already an answer about it, I didn't find it !
Have a nice day !
UPDATE
See this stackbliz
There is one html element that breaks your structure:
<span class="handle">O</span>
ng2-dragula gets wrong index when handles drop event
drake.on('drop', (dropElm: any, target: any, source: any) => {
if (!drake.models || !target) {
return;
}
dropIndex = this.domIndexOf(dropElm, target);
https://github.com/valor-software/ng2-dragula/blob/master/src/components/dragula.provider.ts#L78
target here is your div.drag-container that includes container.items.length + 1 elements.
After that new undefined element is added to your container.items,
To fix it I would suggest you wrapping dragging elements in its own container like:
...
<span class="handle">O</span>
<div [dragula]='"blocks"' [dragulaModel]='container.items'>
<div *ngFor="let item of container.items;" class="drag-bloc">
<span class="handleF">X</span> {{item}}
</div>
</div>
Forked stackblitz example

dynamically insert div after row containing floating divs jquery [duplicate]

I have a number of divs floating in several rows. The divs contain text-previews and a link to slide down the full content (see http://jsfiddle.net/yDcKu/ for an example).
What happens now: When you slide down the content-div it opens right after the connected preview.
What I want to happen: Open the content-div after the last div in the row.
I assume the could be done by:
1. find out which div is the last one in the row of the activated preview,
2. add an id to this div and
3. append the content-div to this div.
I have a solution for steps 2 und 3 using jQuery but no guess how to do the first step.
I can manage to get the document width and the x- and y-value of each div but I have no idea how to find out which div has the highest x- as well the highest y-value and as well is in the row of the activated preview-div.
Any idea anyone? Thanks
Here is an example that does what you want. I simplified your code, so you don't have to manually ID every entry and preview.
http://jsfiddle.net/jqmPc/1/
It's a little complicated. Let me know if you have questions.
Basically, when the window is resized, the script goes through and finds the first preview in each row by finding the preview with the same left offset as the very first one. It then adds a class last to the entry before (previous row) and class first to this preview. I do css clear: left on both of these so that everything wraps normally when the entries open.
I made your code generic, without IDs:
<div class="preview">
<p>Some preview text <a class="trigger" href="#">…</a></p>
</div>
<div class="entry">
<div class="close_button">
<a class="close" href="#">×</a>
</div>
<p>Some content text.</p>
</div>
This makes you not have to write the same code over and over.
The open/close script:
$('.trigger').click(function() {
$('.openEntry').slideUp(800); // Close the open entry
var preview = $(this).closest('.preview'); // Grab the parent of the link
// Now, clone the entry and stick it after the "last" item on this row:
preview.next('.entry').clone().addClass('openEntry').insertAfter(preview.nextAll('.last:first')).slideDown(800);
});
// Use "on()" here, because the "openEntry" is dynamically added
// (and it's good practice anyway)
$('body').on('click', '.close', function() {
// Close and remove the cloned entry
$('.openEntry').slideUp(800).remove();
});
This could be simplified a bit I'm sure, especially if you were willing to reformat your html a little more, by putting the entry inside of the preview element (but still hidden). Here is a slightly simpler version, with the html rearranged:
http://jsfiddle.net/jqmPc/2/
(I also color the first and last element on the line so you can see what is going on)
You could just get the last div in the array after calling getElementsByTagName.
var divArray = wrapperDiv.getElementsByTagName("div");
if(divArray.length > 0)
var lastDiv = divArray[divArray.length-1];
else
console.log("Empty!");
i am not able to correctly understand your question, but if you want to find out last div element in the document then you can do something like this
$("div:last")
so this will give you last div of the document
Reference:
http://api.jquery.com/last-selector/
$([1,2]).each(function(idx,el) {
$("#entry" + el).hide().insertAfter("div.entry:last");
$("#trigger" + el).click(function() {
$("#entry" + el).slideDown('800');
});
$("#close" + el).click(function() {
$("#entry" + el).slideUp('800');
});
});​
http://jsfiddle.net/yDcKu/11/
I got same problem as yours, and I have been redirected to this question. But I think the answer is too complicated to my need. So I made my own way. Supposedly, you get your div list from a JSON, you can do this:
product[0] = {id: "name1", text: "text1"}
product[1] = {id: "name2", text: "text2"}
product[2] = {id: "name3", text: "text3"}
private getLastElement(id, products) {
const getTop = (id) => $("#" + id).position().top;
let itemPos = getTop(id);
let itemIndex = products.map(x => x.id).indexOf(id);
let lastID = itemIndex;
while (lastID < products.length - 1) {
if (getTop(products[lastID + 1].id) > itemPos) break;
lastID++;
}
return products[lastID].id;
}
But you can also find out by gathering all id inside your wrapper.
It works by scanning next id's row position, and return the last id of the same row.
I was looking for the same but on a single element to add style on first and last elements. #Jeff B answer helped me so alter and use it for me on one element. So the search phrase 'Get the last div in a row of floating divs' and someone looking for the same, this code may helpful:
Fiddle: https://jsfiddle.net/kunjsharma/qze8n97x/2/
JS:
$(function() {
$(window).on('resize', function() {
var startPosX = $('.preview:first').position().left;
$('.preview').removeClass("first last");
$('.preview').each(function() {
if ($(this).position().left == startPosX) {
$(this).addClass("first");
$(this).prevAll('.preview:first').addClass("last");
}
});
$('.preview:last').addClass("last");
});
$(window).trigger('resize');
});
CSS:
.preview {
float: left;
}
HTML:
<div class="preview">
<p>Some preview text</p>
</div>
<div class="preview">
<p>Some preview text</p>
</div>
<div class="preview">
<p>Some preview text</p>
</div>
<div class="preview">
<p>Some preview text</p>
</div>

Get the last div in a row of floating divs

I have a number of divs floating in several rows. The divs contain text-previews and a link to slide down the full content (see http://jsfiddle.net/yDcKu/ for an example).
What happens now: When you slide down the content-div it opens right after the connected preview.
What I want to happen: Open the content-div after the last div in the row.
I assume the could be done by:
1. find out which div is the last one in the row of the activated preview,
2. add an id to this div and
3. append the content-div to this div.
I have a solution for steps 2 und 3 using jQuery but no guess how to do the first step.
I can manage to get the document width and the x- and y-value of each div but I have no idea how to find out which div has the highest x- as well the highest y-value and as well is in the row of the activated preview-div.
Any idea anyone? Thanks
Here is an example that does what you want. I simplified your code, so you don't have to manually ID every entry and preview.
http://jsfiddle.net/jqmPc/1/
It's a little complicated. Let me know if you have questions.
Basically, when the window is resized, the script goes through and finds the first preview in each row by finding the preview with the same left offset as the very first one. It then adds a class last to the entry before (previous row) and class first to this preview. I do css clear: left on both of these so that everything wraps normally when the entries open.
I made your code generic, without IDs:
<div class="preview">
<p>Some preview text <a class="trigger" href="#">…</a></p>
</div>
<div class="entry">
<div class="close_button">
<a class="close" href="#">×</a>
</div>
<p>Some content text.</p>
</div>
This makes you not have to write the same code over and over.
The open/close script:
$('.trigger').click(function() {
$('.openEntry').slideUp(800); // Close the open entry
var preview = $(this).closest('.preview'); // Grab the parent of the link
// Now, clone the entry and stick it after the "last" item on this row:
preview.next('.entry').clone().addClass('openEntry').insertAfter(preview.nextAll('.last:first')).slideDown(800);
});
// Use "on()" here, because the "openEntry" is dynamically added
// (and it's good practice anyway)
$('body').on('click', '.close', function() {
// Close and remove the cloned entry
$('.openEntry').slideUp(800).remove();
});
This could be simplified a bit I'm sure, especially if you were willing to reformat your html a little more, by putting the entry inside of the preview element (but still hidden). Here is a slightly simpler version, with the html rearranged:
http://jsfiddle.net/jqmPc/2/
(I also color the first and last element on the line so you can see what is going on)
You could just get the last div in the array after calling getElementsByTagName.
var divArray = wrapperDiv.getElementsByTagName("div");
if(divArray.length > 0)
var lastDiv = divArray[divArray.length-1];
else
console.log("Empty!");
i am not able to correctly understand your question, but if you want to find out last div element in the document then you can do something like this
$("div:last")
so this will give you last div of the document
Reference:
http://api.jquery.com/last-selector/
$([1,2]).each(function(idx,el) {
$("#entry" + el).hide().insertAfter("div.entry:last");
$("#trigger" + el).click(function() {
$("#entry" + el).slideDown('800');
});
$("#close" + el).click(function() {
$("#entry" + el).slideUp('800');
});
});​
http://jsfiddle.net/yDcKu/11/
I got same problem as yours, and I have been redirected to this question. But I think the answer is too complicated to my need. So I made my own way. Supposedly, you get your div list from a JSON, you can do this:
product[0] = {id: "name1", text: "text1"}
product[1] = {id: "name2", text: "text2"}
product[2] = {id: "name3", text: "text3"}
private getLastElement(id, products) {
const getTop = (id) => $("#" + id).position().top;
let itemPos = getTop(id);
let itemIndex = products.map(x => x.id).indexOf(id);
let lastID = itemIndex;
while (lastID < products.length - 1) {
if (getTop(products[lastID + 1].id) > itemPos) break;
lastID++;
}
return products[lastID].id;
}
But you can also find out by gathering all id inside your wrapper.
It works by scanning next id's row position, and return the last id of the same row.
I was looking for the same but on a single element to add style on first and last elements. #Jeff B answer helped me so alter and use it for me on one element. So the search phrase 'Get the last div in a row of floating divs' and someone looking for the same, this code may helpful:
Fiddle: https://jsfiddle.net/kunjsharma/qze8n97x/2/
JS:
$(function() {
$(window).on('resize', function() {
var startPosX = $('.preview:first').position().left;
$('.preview').removeClass("first last");
$('.preview').each(function() {
if ($(this).position().left == startPosX) {
$(this).addClass("first");
$(this).prevAll('.preview:first').addClass("last");
}
});
$('.preview:last').addClass("last");
});
$(window).trigger('resize');
});
CSS:
.preview {
float: left;
}
HTML:
<div class="preview">
<p>Some preview text</p>
</div>
<div class="preview">
<p>Some preview text</p>
</div>
<div class="preview">
<p>Some preview text</p>
</div>
<div class="preview">
<p>Some preview text</p>
</div>

Categories

Resources