Is there any way to make a div appear stacked on top of another one, by clicking on a button/image?
The problem I have:
I want a poster to appear if I click on it in the selector. I want it to stay, once I click on another poster, which then will stack on top of the previous one. Is it possible to make this a loop, so no matter how often I click different objects the old divs will always stay below the newest one?
(See pictures attached)
first click:
second click:
and so on...
Thanks in advance!
I will cut down the explaination to some documentations such as how to create an element through JS. Teachign every single step would blow the scope of Stackoverflow.
If you know how to create an element through JS you need a variable such as counter in my code that is raised by every single time you run the script with: counter = counter + 1
You set a z-index through JS with element.style.zIndex = variableand thanks to the rising counter it will set the new element over the other element (z-axis-wise).
To rotate the element you can use transform: rotate() which I randomized by calling a variable: rotation = Math.round(Math.random() * 40 - 20); which will randomize a number between -20 and 20.
var preview = document.querySelector('.preview'),
counter = '1';
picture = ''
function addRed() {
picture = 'red';
addPicture();
}
function addGreen() {
picture = 'green';
addPicture();
}
function addBlue() {
picture = 'blue';
addPicture();
}
function addYellow() {
picture = 'yellow';
addPicture();
}
function addPicture() {
var img = document.createElement('img'),
rotation = Math.round(Math.random() * 40 - 20);
switch (picture) {
case 'red':
img.src = 'https://via.placeholder.com/350.jpg/FF0000';
break;
case 'green':
img.src = 'https://via.placeholder.com/350.jpg/00FF00';
break;
case 'blue':
img.src = 'https://via.placeholder.com/350.jpg/0000FF';
break;
case 'yellow':
img.src = 'https://via.placeholder.com/350.jpg/FFFF00';
break;
}
img.style.position = 'absolute';
img.style.top = '50%';
img.style.left = '50%';
img.style.transform = 'translate(-50%, -50%) rotate(' + rotation + 'deg)';
img.style.zIndex = counter;
preview.appendChild(img);
var counter = counter + 1;
}
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 10px;
}
.preview {
grid-column: 1 / -1;
aspect-ratio: 1 / 1;
position: relative;
}
.selector {
padding: 10px;
}
.selector > img {
display: block;
object-fit: contain;
width: 100%;
}
<div class="container">
<div class="preview"></div>
<div class="selector"><img src="https://via.placeholder.com/100.jpg/FF0000" onclick="addRed()"></div>
<div class="selector"><img src="https://via.placeholder.com/100.jpg/00FF00" onclick="addGreen()"></div>
<div class="selector"><img src="https://via.placeholder.com/100.jpg/0000FF" onclick="addBlue()"></div>
<div class="selector"><img src="https://via.placeholder.com/100.jpg/FFFF00" onclick="addYellow()"></div>
</div>
Create a div called preview, and makes its position relative. This is necessary because this creates a new stacking context.
Create another container that will hold the list of images to choose from. I choose a section with an unordered list which will contain these images as list elements.
Set overflow-x auto, so a scrollbar appears once the amount of images exceed the container width.
In your stylesheet, select the images which are child of our preview div, and make their position absolute, with a top margin of 0. This makes sure that all images are stacked in the same x-y direction. This works because the preview div also has display: flex, with align-items: center as well as justify-content: center.
In your script file, select these list images - I called them posters.
const posters = document.querySelectorAll(".poster");
Create two variables called zIndex ( = 1) and rotated ( = true).
Attach an event listener to each of the poster image inside this list of images, using a for...of loop.
for (const poster of posters) {
poster.addEventListener("click", () => onPosterClicked(poster));
}
In this event handler, we select the preview div, and clone the clicked image using cloneNode() method. We also give it class of shown so, our styling for images inside preview work.
We then increment the z-index, and assign this incremented z-index to this cloned image's style.
cloned.style.zIndex = ++zIndex;
For decorational purposes, we switch between rotated and non-rotated transforms, so each image new image added is rotated differently to our previous image.
if (rotated) {
cloned.style.transform = "rotate(-5deg)";
}
rotated = !rotated;
Note: We can achieve this rotation directly through CSS, by using nth-child selectors.
Finally, we append this cloned image to our preview div.
preview.appendChild(cloned);
The complete code looks like this:
function onPosterClicked(poster) {
const preview = document.querySelector(".preview");
const cloned = poster.cloneNode();
cloned.classList = "shown";
cloned.style.zIndex = ++zIndex;
if (rotated) {
cloned.style.transform = "rotate(-5deg)";
}
rotated = !rotated;
preview.appendChild(cloned);
}
const posters = document.querySelectorAll(".poster");
let zIndex = 1;
let rotated = true;
for (const poster of posters) {
poster.addEventListener("click", () => onPosterClicked(poster));
}
*,
*::before,
*::after {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.preview {
width: 100%;
height: 70vh;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.shown {
height: 100%;
position: absolute;
top: 0;
border: 2px solid black;
}
.list {
width: 100%;
padding: 4rem 2rem;
}
.list ul {
display: flex;
flex-flow: row nowrap;
gap: 5px;
overflow-x: auto;
list-style: none;
}
.poster {
max-height: 300px;
object-fit: cover;
}
<body>
<div class="preview">
<img class="shown" src="https://picsum.photos/seed/picsum/200/300" alt="test image" />
</div>
<section class="list">
<ul>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
<li>
<img class="poster" src="https://picsum.photos/seed/picsum/200/300" alt="sample poster image" />
</li>
</ul>
</section>
<script async defer src="main.js"></script>
</body>
Related
I want to rotate the image, but it is going out of parent div.
<div>
<img src="https://cdn.eso.org/images/thumb300y/eso1907a.jpg">
<button class="rotate-button">rotate image</button>
</div>
jquery code
$('.rotate-button').on('click', function() {
var image = $(this).prev('img');
image.className = "rotated_90deg";
});
unrotated state:
rotated state:
how can I keep the image smaller in rotated state, so that it does not go out of parent div?
Try using the solution with scale property
$('.rotate-button').on('click', function() {
var image = $(this).prev('img');
image.className = "rotated_90deg";
});
.rotated_90deg {
transform: rotate(90deg) scale(0.5, 1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<img src="https://cdn.eso.org/images/thumb300y/eso1907a.jpg">
<button class="rotate-button">rotate image</button>
</div>
"tranform rotate" does just that. It retains its original height, and the forging is done in a separate visual layer.
the best thing you can do is set the height of the area where the image rotates equal to the largest side of the image
const img = document.querySelector('img');
const {offsetHeight, offsetWidth} = img;
if(offsetWidth >= offsetHeight) {
img.parentElement.style.height = offsetWidth + 'px';
}
const rotations = [];
const rotateImage = () => {
rotations.push('rotate(45deg)');
img.style.transform = rotations.join(' ');
}
div { display: flex; }
img { transition: .3s; margin: auto; }
button { display: block; margin: auto; position: relative }
<div>
<img src="http://placekitten.com/300/200">
</div>
<button onclick=rotateImage()>Rotate</button>
hmm ... maybe I hastened to answer.
As a solution, "position: relative;" on the button
Put the image inside a container div, give it an id or class and set the overflow to hidden:
.imgContainer{
overflow: hidden;
}
Or if you want the picture to scale so it fits within the div, set max width and height:
.imgContainer img{
max-width: 100%;
max-height: 100%;
}
How can I position several <img> elements into a circle around another and have those elements all be clickable links as well? I want it to look like the picture below, but I have no idea how to achieve that effect.
Is this even possible?
2020 solution
Here's a more modern solution I use these days.
I start off by generating the HTML starting from an array of images. Whether the HTML is generated using PHP, JS, some HTML preprocessor, whatever... this matters less as the basic idea behind is the same.
Here's the Pug code that would do this:
//- start with an array of images, described by url and alt text
- let imgs = [
- {
- src: 'image_url.jpg',
- alt: 'image alt text'
- } /* and so on, add more images here */
- ];
- let n_imgs = imgs.length;
- let has_mid = 1; /* 0 if there's no item in the middle, 1 otherwise */
- let m = n_imgs - has_mid; /* how many are ON the circle */
- let tan = Math.tan(Math.PI/m); /* tangent of half the base angle */
.container(style=`--m: ${m}; --tan: ${+tan.toFixed(2)}`)
- for(let i = 0; i < n_imgs; i++)
a(href='#' style=i - has_mid >= 0 ? `--i: ${i}` : null)
img(src=imgs[i].src alt=imgs[i].alt)
The generated HTML looks as follows (and yes, you can write the HTML manually too, but it's going to be a pain to make changes afterwards):
<div class="container" style="--m: 8; --tan: 0.41">
<a href='#'>
<img src="image_mid.jpg" alt="alt text"/>
</a>
<a style="--i: 1">
<img src="first_img_on_circle.jpg" alt="alt text"/>
</a>
<!-- the rest of those placed on the circle -->
</div>
In the CSS, we decide on a size for the images, let's say 8em. The --m items are positioned on a circle and it's if they're in the middle of the edges of a polygon of --m edges, all of which are tangent to the circle.
If you have a hard time picturing that, you can play with this interactive demo which constructs the incircle and circumcircle for various polygons whose number of edges you pick by dragging the slider.
This tells us that the size of the container must be twice the radius of the circle plus twice half the size of the images.
We don't yet know the radius, but we can compute it if we know the number of edges (and therefore the tangent of half the base angle, precomputed and set as a custom property --tan) and the polygon edge. We probably want the polygon edge to be a least the size of the images, but how much we leave on the sides is arbitrary. Let's say we have half the image size on each side, so the polygon edge is twice the image size. This gives us the following CSS:
.container {
--d: 6.5em; /* image size */
--rel: 1; /* how much extra space we want between images, 1 = one image size */
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan)); /* circle radius */
--s: calc(2*var(--r) + var(--d)); /* container size */
position: relative;
width: var(--s); height: var(--s);
background: silver /* to show images perfectly fit in container */
}
.container a {
position: absolute;
top: 50%; left: 50%;
margin: calc(-.5*var(--d));
width: var(--d); height: var(--d);
--az: calc(var(--i)*1turn/var(--m));
transform:
rotate(var(--az))
translate(var(--r))
rotate(calc(-1*var(--az)))
}
img { max-width: 100% }
See the old solution for an explanation of how the transform chain works.
This way, adding or removing an image from the array of images automatically arranges the new number of images on a circle such that they're equally spaced out and also adjusts the size of the container. You can test this in this demo.
OLD solution (preserved for historical reasons)
Yes, it is very much possible and very simple using just CSS. You just need to have clear in mind the angles at which you want the links with the images (I've added a piece of code at the end just for showing the angles whenever you hover one of them).
You first need a wrapper. I set its diameter to be 24em (width: 24em; height: 24em; does that), you can set it to whatever you want. You give it position: relative;.
You then position your links with the images in the center of that wrapper, both horizontally and vertically. You do that by setting position: absolute; and then top: 50%; left: 50%; and margin: -2em; (where 2em is half the width of the link with the image, which I've set to be 4em - again, you can change it to whatever you wish, but don't forget to change the margin in that case).
You then decide on the angles at which you want to have your links with the images and you add a class deg{desired_angle} (for example deg0 or deg45 or whatever). Then for each such class you apply chained CSS transforms, like this:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
where you replace {desired_angle} with 0, 45, and so on...
The first rotate transform rotates the object and its axes, the translate transform translates the object along the rotated X axis and the second rotate transform brings back the object into position.
The advantage of this method is that it is flexible. You can add new images at different angles without altering the current structure.
CODE SNIPPET
.circle-container {
position: relative;
width: 24em;
height: 24em;
padding: 2.8em;
/*2.8em = 2em*1.4 (2em = half the width of a link with img, 1.4 = sqrt(2))*/
border: dashed 1px;
border-radius: 50%;
margin: 1.75em auto 0;
}
.circle-container a {
display: block;
position: absolute;
top: 50%; left: 50%;
width: 4em; height: 4em;
margin: -2em;
}
.circle-container img { display: block; width: 100%; }
.deg0 { transform: translate(12em); } /* 12em = half the width of the wrapper */
.deg45 { transform: rotate(45deg) translate(12em) rotate(-45deg); }
.deg135 { transform: rotate(135deg) translate(12em) rotate(-135deg); }
.deg180 { transform: translate(-12em); }
.deg225 { transform: rotate(225deg) translate(12em) rotate(-225deg); }
.deg315 { transform: rotate(315deg) translate(12em) rotate(-315deg); }
<div class='circle-container'>
<a href='#' class='center'><img src='image.jpg'></a>
<a href='#' class='deg0'><img src='image.jpg'></a>
<a href='#' class='deg45'><img src='image.jpg'></a>
<a href='#' class='deg135'><img src='image.jpg'></a>
<a href='#' class='deg180'><img src='image.jpg'></a>
<a href='#' class='deg225'><img src='image.jpg'></a>
<a href='#' class='deg315'><img src='image.jpg'></a>
</div>
Also, you could further simplify the HTML by using background images for the links instead of using img tags.
EDIT: example with fallback for IE8 and older (tested in IE8 and IE7)
Here is the easy solution without absolute positioning:
.container .row {
margin: 20px;
text-align: center;
}
.container .row img {
margin: 0 20px;
}
<div class="container">
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
</div>
http://jsfiddle.net/mD6H6/
Using the solution proposed by #Ana:
transform: rotate(${angle}deg) translate(${radius}px) rotate(-${angle}deg)
I created the following jsFiddle that places circles dynamically using plain JavaScript (jQuery version also available).
The way it works is rather simple:
document.querySelectorAll( '.ciclegraph' ).forEach( ( ciclegraph )=>{
let circles = ciclegraph.querySelectorAll( '.circle' )
let angle = 360-90, dangle = 360 / circles.length
for( let i = 0; i < circles.length; ++i ){
let circle = circles[i]
angle += dangle
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth / 2}px) rotate(-${angle}deg)`
}
})
.ciclegraph {
position: relative;
width: 500px;
height: 500px;
margin: calc(100px / 2 + 0px);
}
.ciclegraph:before {
content: "";
position: absolute;
top: 0; left: 0;
border: 2px solid teal;
width: calc( 100% - 2px * 2);
height: calc( 100% - 2px * 2 );
border-radius: 50%;
}
.ciclegraph .circle {
position: absolute;
top: 50%; left: 50%;
width: 100px;
height: 100px;
margin: calc( -100px / 2 );
background: teal;
border-radius: 50%;
}
<div class="ciclegraph">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
Building off #Ana's excellent answer, I created this dynamic version that allows you to add and remove elements from the DOM and maintain proportionate spacing between the elements - check out my fiddle: https://jsfiddle.net/skwidbreth/q59s90oy/
var list = $("#list");
var updateLayout = function(listItems) {
for (var i = 0; i < listItems.length; i++) {
var offsetAngle = 360 / listItems.length;
var rotateAngle = offsetAngle * i;
$(listItems[i]).css("transform", "rotate(" + rotateAngle + "deg) translate(0, -200px) rotate(-" + rotateAngle + "deg)")
};
};
$(document).on("click", "#add-item", function() {
var listItem = $("<li class='list-item'>Things go here<button class='remove-item'>Remove</button></li>");
list.append(listItem);
var listItems = $(".list-item");
updateLayout(listItems);
});
$(document).on("click", ".remove-item", function() {
$(this).parent().remove();
var listItems = $(".list-item");
updateLayout(listItems);
});
#list {
background-color: blue;
height: 400px;
width: 400px;
border-radius: 50%;
position: relative;
}
.list-item {
list-style: none;
background-color: red;
height: 50px;
width: 50px;
position: absolute;
top: 50%;
left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<ul id="list"></ul>
<button id="add-item">Add item</button>
Here is a version I made in React from the examples here.
CodeSandbox Example
import React, { useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const graph = useRef(null);
useEffect(() => {
const ciclegraph = graph.current;
const circleElements = ciclegraph.childNodes;
let angle = 360 - 90;
let dangle = 360 / circleElements.length;
for (let i = 0; i < circleElements.length; i++) {
let circle = circleElements[i];
angle += dangle;
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth /
2}px) rotate(-${angle}deg)`;
}
}, []);
return (
<div className="App">
<div className="ciclegraph" ref={graph}>
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
</div>
</div>
);
}
You can certainly do it with pure css or use JavaScript. My suggestion:
If you already know that the images number will never change just calculate your styles and go with plain css (pros: better performances, very reliable)
If the number can vary either dynamically in your app or just may vary in the future go with a Js solution (pros: more future-proof)
I had a similar job to do, so I created a script and open sourced it here on Github for anyone who might need it. It just accepts some configuration values and simply outputs the CSS code you need.
If you want to go for the Js solution here's a simple pointer that can be useful to you. Using this html as a starting point being #box the container and .dot the image/div in the middle you want all your other images around:
Starting html:
<div id="box">
<div class="dot"></div>
<img src="my-img.jpg">
<!-- all the other images you need-->
</div>
Starting Css:
#box{
width: 400px;
height: 400px;
position: relative;
border-radius: 100%;
border: 1px solid teal;
}
.dot{
position: absolute;
border-radius: 100%;
width: 40px;
height: 40px;
left: 50%;
top: 50%;
margin-left: -20px;
margin-top: -20px;
background: rebeccapurple;
}
img{
width: 40px;
height: 40px;
position: absolute;
}
You can create a quick function along these lines:
var circle = document.getElementById('box'),
imgs = document.getElementsByTagName('img'),
total = imgs.length,
coords = {},
diam, radius1, radius2, imgW;
// get circle diameter
// getBoundingClientRect outputs the actual px AFTER transform
// using getComputedStyle does the job as we want
diam = parseInt( window.getComputedStyle(circle).getPropertyValue('width') ),
radius = diam/2,
imgW = imgs[0].getBoundingClientRect().width,
// get the dimensions of the inner circle we want the images to align to
radius2 = radius - imgW
var i,
alpha = Math.PI / 2,
len = imgs.length,
corner = 2 * Math.PI / total;
// loop over the images and assign the correct css props
for ( i = 0 ; i < total; i++ ){
imgs[i].style.left = parseInt( ( radius - imgW / 2 ) + ( radius2 * Math.cos( alpha ) ) ) + 'px'
imgs[i].style.top = parseInt( ( radius - imgW / 2 ) - ( radius2 * Math.sin( alpha ) ) ) + 'px'
alpha = alpha - corner;
}
You can see a live example here
There is no way to magically place clickable items in a circle around another element with CSS.
The way how I would do this is by using a container with position:relative;. And then place all the elements with position:absolute; and using top and left to target it's place.
Even though you haven't placed jquery in your tags it might be best to use jQuery / javascript for this.
First step is placing your center image perfectly in the center of the container using position:relative;.
#centerImage {
position:absolute;
top:50%;
left:50%;
width:200px;
height:200px;
margin: -100px 0 0 -100px;
}
After that you can place the other elements around it by using an offset() of the centerImage minus the offset() of the container. Giving you the exact top and left of the image.
var left = $('#centerImage').offset().left - $('#centerImage').parent().offset().left;
var top = $('#centerImage').offset().top - $('#centerImage').parent().offset().top;
$('#surroundingElement1').css({
'left': left - 50,
'top': top - 50
});
$('#surroundingElement2').css({
'left': left - 50,
'top': top
});
$('#surroundingElement3').css({
'left': left - 50,
'top': top + 50
});
What I've done here is placing the elements relative to the centerImage. Hope this helps.
You could do it like this: fiddle
Don't mind the positioning, its a quick example
The first step is to have 6 long columnar boxes:
The second step is to use position: absolute and move them all into the middle of your container:
And now rotate them around the pivot point located at the bottom center. Use :nth-child to vary rotation angles:
div {
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
rotate: (360deg / 6) * $n;
}
}
Now all you have to do is to locate your images at the far end of every column, and compensate the rotation with an anti-rotation :)
Full source:
<div class="flower">
<div class="petal">1</div>
<div class="petal">2</div>
<div class="petal">3</div>
<div class="petal">4</div>
<div class="petal">5</div>
<div class="petal">6</div>
</div>
.flower {
width: 300px;
height: 300px;
// We need a relative position
// so that children can have "position:abolute"
position: relative;
.petal {
// Make sure petals are visible
border: 1px solid #999;
// Position them all in one point
position: absolute; top: 0; left: 50%;
display: inline-block;
width: 30px; height: 150px;
// Rotation
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
// Petal rotation
$angle: (360deg / 6) * $n;
rotate: $angle;
// Icon anti-rotation
.icon { rotate: -$angle; }
}
}
}
}
See CodePen
This question already has answers here:
Can I use javascript to dynamically change a video's source?
(10 answers)
Closed 3 years ago.
I have a html5 video loaded on my page which looks like this:
'
<div class="row">
<video controls class="vid-center" poster="<?php echo get_template_directory_uri(); ?>/img/video-bg.png" id="video-id">
<div class="video-overlay">logo and play bitton here logo and play bitton here </div>
<source src="http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4" type="video/mp4">
<source src="movie.ogg" type="video/ogg">
Your browser does not support the video tag. </video>
</div>
<div class="video-cont"></div>
</section>
<div class="row">
<div class="large-3 small-4 columns">
<img src="<?php echo get_template_directory_uri(); ?>/img/thumb-vid1.png" alt=""/>
<h4>Video name 1</h4>
<p>Lorum ipsum neg reyo sum tomenyen</p>
</div>
<div class="large-3 small-4 columns">
<img src="<?php echo get_template_directory_uri(); ?>/img/thumb-vid2.png" alt=""/>
<h4>Video name 1</h4>
<p>Lorum ipsum neg reyo sum tomenyen</p>
</div>
<div class="large-3 small-4 columns">
<img src="<?php echo get_template_directory_uri(); ?>/img/thumb-vid3.png" alt=""/>
<h4>Video name 1</h4>
<p>Lorum ipsum neg reyo sum tomenyen</p>
</div>
<div class="large-3 hide-for-small-only columns">
<img src="<?php echo get_template_directory_uri(); ?>/img/thumb-vid4.png" alt=""/>
<h4>Video name 1</h4>
<p>Lorum ipsum neg reyo sum tomenyen</p>
</div>
</div>
'
I am trying to create a basic gallery so when a user selects / clicks / presses one of the thumbnail images it will load a new video into the video player i.e change the video source depending on which img / div is clicked. If thumbnail clicked then load the string src into the player and autoplay.
Is this possible to do in JavaScript to change the current video src?
Any help would be greatly appreciated.
This demo is twofold:
The top half features four divs, which upon loading will dynamically have a video each.
The bottom half has one video and a playlist. The video element will play whatever is clicked on the playlist.
What they have in common is that they are sharing the same video and image files. Basically, this demonstration shows 2 ways to go about having multiple videos on a page.
The top example starts of as 4 normal divs.
loadPlayers() is called on pageload. It will:
Collect the 4 divs into a NodeList.
Convert the NodeList into an array.
Array.prototype.map.call() will also call an anonymous function that will gather each of the divs ids and the pass them into the next function...
struct(ID) is responsible for building the video elements.
The ids from loadPlayers() is now processed into strings that will define values for src for each video.
The video and source elements are made with createElement()
The attributes are added by setAttribute()
Finally, each video is added into it's corresponding div with appendChild().
The bottom example dynamically creates a playlist from an array of strings provided by the developer (this can be modified so the user can interact with creating a playlist.)
generateList(arr, str) requires an array of strings and an element to add playlist items into. The first argument, arr is an array, each string representing the file name of a video/image (sans extension). The second argument is a string that must be syntactically like a selector. ex:
<div id="vid3"></div> = "#vid3"
<nav></nav> = "nav"
<main class="box"></main> = ".box"
<input type="number"/> = "input[type='number']"
Although this argument can be any element, it's suggested that a ul, ol, dl, or nav are better suited to host the playlist items.
Using array.map() to apply the function eachItem() to each array element, it is then returned as a complete playlist; each item is an anchor with a clipped background image.
When each item is clicked, the eventListener will trigger the function playSelect()
PLUNKER
README.md
Snippet
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Video Gallery</title>
<style>
html,
body {
width: 100%;
height: 100%;
font: 400 16px/1.45 'Verdana';
}
body {
position: relative;
background: #111;
color: #EEE;
}
.gallery {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
width: 100vw;
height: 50vh;
}
#sec2 {
justify-content: center;
}
.vid {
width: 240px;
flex 0 2 auto;
}
.solo {
width: 480px;
flex: 0 2 auto;
}
video {
width: 100%;
height: auto;
}
.list {
border: 3px inset #fc3;
margin: 0 10px;
}
.list a {
text-decoration: none;
cursor: pointer;
display: block;
width: 75px;
height: 75px;
background-repeat: no-repeat;
background-size: cover;
color: #00f;
font-size: 1rem;
border-bottom: 1px solid #fc0;
}
.list a:hover {
color: #0ff;
}
#media screen and (max-width: 768px) {
#sec1 {
justify-content: flex-start;
}
#sec2 {
justify-content: flex-end;
}
.vid {
flex: 0 2 auto;
width: 160px;
}
.solo {
flex: 0 2 auto;
width: 320px;
}
}
</style>
</head>
<body>
<section id="sec1" class="gallery">
<div id="vid1" class="vid"></div>
<div id="vid2" class="vid"></div>
<div id="vid3" class="vid"></div>
<div id="vid4" class="vid"></div>
</section>
<section id="sec2" class="gallery">
<div id="vid5" class="solo">
<video id="vid5v" poster="https://glpjt.s3.amazonaws.com/so/av/vid5.png" controls=true>
<source src="https://glpjt.s3.amazonaws.com/so/av/vid5.mp4" type="video/mp4">
</video>
</div>
<nav id="vNav5" class="list"></nav>
</section>
<script>
var vNav5 = document.getElementById('vNav5');
var playlist = ['vid1', 'vid2', 'vid3', 'vid4'];
function loadPlayers() {
var divs = document.querySelectorAll('.vid');
var ids = Array.prototype.map.call(divs, function(obj) {
var ID = obj.id;
return vStruct(ID);
});
}
function vStruct(id) {
var vTag = document.createElement('video');
var vSrc = document.createElement('source');
var vDiv = document.getElementById(id);
var vIDv = id.toString();
vTag.id = vIDv + 'v';
var vUrl = 'https://glpjt.s3.amazonaws.com/so/av/';
var vPng = vUrl + id + '.png';
var vMp4 = vUrl + id + '.mp4';
vTag.setAttribute('poster', vPng);
vTag.setAttribute('controls', true);
vSrc.setAttribute('src', vMp4);
vSrc.setAttribute('type', 'video/mp4');
vDiv.appendChild(vTag);
vTag.appendChild(vSrc);
}
function generateList(vArr, vStr) {
var vTgt = document.querySelector(vStr);
var lArr = vArr.map(eachLink);
lArr.forEach(function(obj) {
vTgt.appendChild(obj);
});
}
function eachLink(id) {
var ID = id.toString();
var vUrl = 'https://glpjt.s3.amazonaws.com/so/av/';
var vLink = document.createElement('a');
vLink.setAttribute('href', vUrl + ID + '.mp4');
vLink.textContent = ID;
vLink.style.backgroundImage = "url('" + vUrl + ID + ".png')";
return vLink;
}
vNav5.addEventListener('click', playSelect, false);
function playSelect(e) {
e.preventDefault();
if (e.currentTarget !== e.target) {
var choice = e.target.href;
var parent = e.target.parentElement;
var uncle = parent.previousElementSibling;
var vid = uncle.querySelector('video');
if (vid.playing) {
vid.pause();
}
vid.src = "";
vid.src = choice;
vid.load();
vid.play();
e.stopPropagation();
}
}
loadPlayers();
generateList(playlist, '#vNav5');
</script>
</body>
</html>
I am trying to include a caption on the actual webpage under the image while using the magnificence popup gallery. Using a div and class caption or carousel-caption, I am unable to do so without the images in the gallery stacking vertically one by one. How can I do this?
<a href="img/base/ggg.PNG" title="HELLO" class="chicken">
<img src="img/base/pop.PNG" alt="remember your alt tag" />
</a>
$(document).ready(function() {
$('.chicken').magnificPopup({
type: 'image',
gallery:{enabled:true}
// other options here
// end each line (except the last) with a comma
});
});
js fiddle: https://jsfiddle.net/sb4btox7
Alright, have a look at this, I adjusted your function a bit:
$(document).ready(function() {
$('.chicken').magnificPopup({
type: 'image',
gallery: {
enabled: true
}
}).each(function() { // Here I chain a loop to each .chicken element
// Now we append the title inside the .chicken element
$(this).append('<div class="title">' + $(this).attr('title') + '</div>');
});
});
The CSS:
.chicken {
display: inline-block;
width: 20%;
margin: 10px;
}
.chicken img {
width: 100%; /* set it to 100% of it's parents width */
vertical-align: bottom;
}
.chicken .title {
text-align: center;
}
and here's the DEMO.
Now you can also just add the title directly to the html like this:
<a href="http://placehold.it/350x250" title="The Title A" class="chicken">
<img src="http://placehold.it/350x250" />
<div class="title">The Title A</div>
</a>
Situation:
I am making an own site, today I was facing a little problem..
On my page I'm using a wrap, in the wrap I'm using three columns, in the middle, the main, I'm using a grey image as a background. In the main I want some text and a slideshow, but the image has to adapt, when I'm using simple text this works(the image is as long as the text).
Problem:
Under my text I want a simple slideshow, nothing really special. But when I place the slideshow in the main, the image does not adapt..? It is not recognised as an element in the main?
Code:
For the html page this is my code:
<div id="main">
<p id="Title">Welcome</p>
<p>Some text here</p>
<p>
<div id="slideshow2">
<div class="slide active"><img src="images/1.png" alt="Slideshow Image 1" /></a>
</div>
<div class="slide"><img src="images/2.png" alt="Slideshow Image 2" /></a>
</div>
<div class="slide"><img src="images/3.png" alt="Slideshow Image 3" /></a>
</div>
</div>
</p>
</div>
I'm using a script to operate the slideshow, this is working perfect, so no need to post this.
In the CSS I'm doing this:
#slideshow2{
position: absolute;
}
#slideshow2 .slide {
position:absolute;
top:0;
left:0;
z-index:8;
opacity:0.0;
}
#slideshow2 div.active {
z-index:10;
opacity:1.0;
}
#slideshow2 div.last-active {
z-index:9;
}
#main{
padding-top: 1px;
padding-bottom: 3px;
padding-left: 5px;
padding-right: 5px;
margin-left: auto;
margin-right: auto;
width: 800px;
background: url("images/main.png") repeat;
z-index: 1;
}
And the wrap:
#wrap{
margin-left: auto;
margin-right: auto;
width: 1240px;
height: auto;
min-height: 100%;
position: relative;
}
I hope someone can help me because I do not know how I can fix this..
Update:
I tried to do this:
<p id="slideshow2">
<div class="slide active"><img src="images/1.png" alt="Slideshow Image 1" /></a>
</div>
<div class="slide"><img src="images/2.png" alt="Slideshow Image 2" /></a>
</div>
<div class="slide"><img src="images/3.png" alt="Slideshow Image 3" /></a>
</div>
</p>
Here the main recognises the slideshow as an element but all the images are placed under eachother and not above eachother..
Script:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
<script type="text/javascript">
function slideSwitch() {
var $active = $('#slideshow div.active');
if ( $active.length == 0 ) $active = $('#slideshow div:last');
var $next = $active.next().length ? $active.next()
: $('#slideshow div:first');
$active.addClass('last-active');
$next.css({opacity: 0.0})
.addClass('active')
.animate({opacity: 1.0}, 1000, function() {
$active.removeClass('active last-active');
});
}
$(function() {
setInterval( "slideSwitch()", 3500 );
});
</script>
Please help :)
Thank you
Does the slideshow script you are using require you to have the anchor close tags without a corresponding opening tag for some reason ? If not, perhaps it is gakking on it.
Also, what do you mean by "adapt" in this question ?
but ok, the problem is that absolutely positioned elements leave the normal document flow. Their height doesn't contribute to the height of their container. So yes, you are correct, they are not recognized as an element within main for the purposes of element height. As it is now, #main will have a height based only on the text outside of #slideshow2.
As a quick fix, try this:
#slideshow2 {
position: relative;
height: 400px;
}
or whatever the height of your images is, if they are consistent
If the size of your slides are variable, add this to slideSwitch() :
var slideHeight = $next.height();
$next.parent().height( slideHeight );
like so:
<script type="text/javascript">
function slideSwitch() {
var $active = $('#slideshow div.active');
if ( $active.length == 0 ) $active = $('#slideshow div:last');
var $next = $active.next().length ? $active.next()
: $('#slideshow div:first');
$active.addClass('last-active');
$next.css({opacity: 0.0})
.addClass('active')
.animate({opacity: 1.0}, 1000, function() {
$active.removeClass('active last-active');
});
}
// set the #slideshow2 height
$next.parent().height( $next.height() );
$(function() {
setInterval( "slideSwitch()", 3500 );
});
</script>
Although maybe this is all nonsense, and the problem is that you've changed the ID of #slideshow to #slideshow2, but your script still references #slideshow
#slideshow2{
position: absolute;
}
Should have a size, since content is absolute too.