please can some one help to figure out why the image not maintaining the aspect ratio. I've was looking for a Flickr script to my site and found one. But the main problem is that it cannot maintain the aspect ratio.
Please help me obi wan, you are my last hope!
Script was found in this site:
https://github.com/blackfalcon/flickr-carousel/blob/master/index.html
To help identify the issues i've uploaded it to my site as index2:
http://scorpion3d.com/index2.html
<!doctype html>
<html lang="en" dir="ltr">
<head>
<title>Flickr API Carousel</title>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link href="https://fonts.googleapis.com/css?family=EB+Garamond" rel="stylesheet">
<style>
html,
body {
height: 100%;
margin: 0;
font-family: 'EB Garamond', serif;
}
html {
box-sizing: border-box;
}
body {
background-color: rgba(0,0,0,0.5);
}
*, *:before, *:after {
box-sizing: inherit;
}
a {
color: white;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.carousel-box {
height: 100%;
overflow: hidden;
}
.action-buttons {
width: 100%;
display: flex;
justify-content: space-between;
position: absolute;
top: calc(50% - 100px);
z-index: 1;
opacity: .1;
transition: opacity .25s ease;
}
.carousel-box:hover .action-buttons {
opacity: 1;
}
.action-buttons button {
border: none;
font-size: 50px;
padding: 0 10px;
display: block;
background-color: rgba(255,255,255,.5);
height: 1.2em;
line-height: 1;
}
.action-buttons button:hover {
cursor: pointer;
}
.carousel-content {
height: 100%;
display: flex;
align-items: center;
}
[data-function="slide"] {
position: absolute;
width: 100%;
height: 100%;
text-align: center;
opacity: 0;
transition: opacity .25s ease;
display: flex;
align-items: flex-end;
}
[data-function="slide"] p {
letter-spacing: 3px;
padding: 1rem 40px;
width: 100%;
color: white;
font-size: 2.5rem;
z-index: 10;
margin: 0 auto;
background-color: rgba(0,0,0,0.3);
}
.carousel-content img {
position: fixed;
top: 0;
bottom: 0;
left: -90%;
right: 0;
height: 100%;
overflow: auto;
}
[data-function="slide"].current-slide {
opacity: 1;
}
.album-info {
color: white;
position: absolute;
background-color: rgba(0,0,0,0.3);
font-size: 1rem;
z-index: 10;
width: 100%;
padding: 1rem;
}
.album-info > a {
margin-right: 1rem;
padding-right: 1rem;
border-right: 1px solid white;
}
#media screen and (min-width: 568px) {
.carousel-content img {
left: 0;
width: 100%;
overflow: auto;
}
}
#media screen and (min-width: 768px) {
.action-buttons button {
font-size: 100px;
}
}
</style>
</head>
<body>
<div class="carousel-box" id="carouselBox">
<div class="action-buttons">
<button class="prev" aria-hidden="true" tabindex="-1" title="use left arrow key">
❮
</button>
<button class="next" aria-hidden="true" tabindex="-1" title="use right arrow key">
❯
</button>
</div>
<div class="album-info">
<a id="albumLink" target="_blank"><span id="albumInfo"></span></a>
<span>Photos by <a id="albumOwnerLink" target="_blank"><span id="owner"></span></a></span>
</div>
<div class="carousel-content" id="carouselContent"></div>
</div>
<script type="text/javascript">
// Flickr configurations
// Obfuscated API key var for demo
const _0x6e6e=["\x65\x66\x63\x38\x33\x64\x63\x63\x64\x37\x63\x31\x64\x30\x61\x65\x39\x33\x66\x34\x61\x61\x37\x61\x66\x62\x39\x37\x31\x66\x63\x65"];const apiKey=_0x6e6e[0]
// To personalize app, replace with your own API key
// const apiKey = '';
const album = '72157688964206172',
albumOwner = '154845055#N05',
flickrUrl = 'https://api.flickr.com/services/rest/',
method = '?method=flickr.photosets.getPhotos&api_key=',
perPage = '20',
formatCallback = '&format=json&nojsoncallback=1',
contentContainer = document.getElementById('carouselContent'),
oReq = new XMLHttpRequest();
// Handle a response from the Flickr API
function reqListener () {
const flickrPhotos = JSON.parse(this.responseText);
console.log(flickrPhotos.photoset);
// Parse response for album and owner information
const ownerName = flickrPhotos.photoset.ownername,
albumTitle = flickrPhotos.photoset.title,
albumUrl = 'https://www.flickr.com/photos/' + albumOwner + '/albums/' + album,
albumOwnerUrl = 'https://www.flickr.com/photos/' + albumOwner;
// append response data to HTML DOM elements
albumInfo.innerHTML = albumTitle;
owner.innerHTML = ownerName;
albumLink.href = albumUrl;
albumOwnerLink.href = albumOwnerUrl;
// Iterate through flickrPhotos in the response
flickrPhotos.photoset.photo.forEach(function(foto) {
// Generate the URL for individual photo based on template
const url = 'https://farm' + foto.farm + '.staticflickr.com/' + foto.server + '/' + foto.id + '_' + foto.secret + '.jpg';
const photoTitle = foto.title;
// Generate the necessary slide markup
// <span data-function="slide">
// <p>title</p>
// <img src="" />
// </span>
const span = document.createElement('span'),
img = document.createElement('img'),
title = document.createElement('p');
// append response data to generated HTML DOM elements
img.src = url;
img.alt = photoTitle;
title.innerHTML = photoTitle;
span.dataset.function = 'slide';
span.appendChild(title);
span.appendChild(img);
// Now append the new slide to the slide container
contentContainer.appendChild(span);
});
// Remote API request has been made and processed, initialize the carousel.
flickrCarousel();
}
// API call to Flickr
oReq.addEventListener("load", reqListener);
oReq.open("GET", flickrUrl + method + apiKey + '&photoset_id=' + album + '&user_id=' + albumOwner + '&per_page=' + perPage + formatCallback);
oReq.send();
// Carousel function
function flickrCarousel () {
// set scoped variables
const carouselBox = document.getElementById('carouselBox'),
prev = carouselBox.querySelector('.prev'),
next = carouselBox.querySelector('.next'),
slides = carouselBox.querySelectorAll('[data-function=slide]'),
deck = slides.length;
let slide = 0,
currentSlide = slides[0];
// Find current slide of array and add selector
currentSlide.classList.add('current-slide');
// slider function
function pushSlide(flip) {
// Use value of array to find node and remove selector
currentSlide.classList.remove('current-slide');
// Using value of current slide, add flip value to determine next slide value
slide = slide + flip;
// allows for full rotation of carousel; if 0 set value to -1 of array length
if (flip === -1 && slide < 0) {
slide = deck - 1;
}
// allows for full rotation of carousel; if max length of array, set to 0
if (flip === 1 && !slides[slide]) {
slide = 0;
}
// determine active slide and add selector
currentSlide = slides[slide];
currentSlide.classList.add('current-slide');
}
// Bind click events to toggle buttons and pass in slide flip value
next.addEventListener('click', () => {
pushSlide(1);
});
prev.addEventListener('click', () => {
pushSlide(-1);
});
// Bind keyboard events to slide triggers
document.addEventListener('keydown', event => {
if( event.keyCode == 39 ) {
pushSlide(1);
}
if( event.keyCode == 37 ) {
pushSlide(-1);
}
});
};
</script>
</body>
</html>
You have mentioned the height of the image to be 100% while defining ".carousel-content img" class.Try as below :
.carousel-content img {
position: fixed;
top: 0;
bottom: 0;
left: -90%;
right: 0;
overflow: auto;
}
Edit
The general thing to be kept in mind while working with the image :
we must not change the proportion of the image to prevent it from being distorted. hence, we can work either on width or on height, keeping the other one set to "Auto".
Definitely, the image will settle itself according to what we have defined for its width and height. So if there won't be enough space to accommodate the whole image, the part of it will be cropped of ( or you say, stays hidden )
Related
So I wrote this small script which has two lists - Finished/Active tasks, and you can either switch a task's list by hitting a button or read more about the task by hitting "more info". when hitting more info there's a new div element created, which I wanted to follow its hosts element Y position, and it halfway works.
So this is how it looks: enter image description here
And here's the bug that im pretty sure happens when ever the scrollable-elements scrollTop isn't 0 (it is created the right way, and whenever I start scrolling it jumps right up and covers the task itself) : enter image description here.
Here's the part of my code that's is incharge of identifying the amount of px scrolled, scroll direction and moving the "more info" div element, as well as the part that's incharge of creating it, I couldn't figure out where is the problem as I'm pretty new to coding overall and I'm not really familiar with css/html either.
createTooltip() {
const tooltipElement = document.createElement('div');
tooltipElement.className = 'card';
const toolTipTemplate = document.getElementById('tooltip');
const tooltipBody = document.importNode(toolTipTemplate.content, true);
tooltipBody.querySelector('p').textContent = this.text;
tooltipElement.append(tooltipBody);
const hostElPosLeft = this.hostElement.offsetLeft;
const hostElPostop = this.hostElement.offsetTop;
const hostElHeight = this.hostElement.clientHeight;
const parentElementScrolling = this.hostElement.parentElement.scrollTop;
const x = hostElPosLeft + 20;
const y = hostElPostop + hostElHeight - parentElementScrolling - 10;
tooltipElement.style.position = 'absolute';
tooltipElement.style.left = x + 'px';
tooltipElement.style.top = y + 'px';
const scrollHandler = () => {
ulElement.addEventListener('scroll', yLogger);
};
tooltipElement.addEventListener('click', this.closeToolTip);
tooltipElement.addEventListener('click', scrollHandler);
this.element = tooltipElement;
const ulElement = this.hostElement.parentElement;
console.log(ulElement);
let pxPosition = [0];
let currentY = y;
const yLogger = () => {
let scrollDirection;
let pxScrolled = 0;
if (pxPosition.length <= 1) {
pxPosition.push(ulElement.scrollTop);
} else {
pxPosition.push(ulElement.scrollTop);
pxPosition.shift(ulElement);
}
console.log(pxPosition);
if (pxPosition[1] < pxPosition[0]) {
scrollDirection = 'up';
pxScrolled = pxPosition[0] - pxPosition[1];
} else if (pxPosition[0] < pxPosition[1]) {
scrollDirection = 'down';
pxScrolled = pxPosition[1] - pxPosition[0];
}
console.log(pxScrolled);
console.log(scrollDirection);
if (scrollDirection === 'down') {
currentY = currentY - pxScrolled;
console.log(currentY);
tooltipElement.style.top = currentY + 'px';
} else {
scrollDirection === 'up';
currentY = currentY + pxScrolled;
console.log(currentY);
tooltipElement.style.top = currentY + 'px';
}
};
this.hostElement.closest('ul').addEventListener('scroll', yLogger);
}
}
I'm adding the HTML and CSS snippets although I don't think they're neccesary since it was written by the course's instructor which im attending.
Here's the HTML snippet:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Project Board</title>
<link rel="stylesheet" href="assets/styles/app.css" />
<script src="assets/scripts/app.js" defer></script>
</head>
<body>
<template id="tooltip">
<h2>More Info</h2>
<p></p>
</template>
<header id="main-header">
<h1>Project Planner</h1>
</header>
<section id="active-projects">
<header>
<h2>Active Projects</h2>
</header>
<ul>
<li
id="p1"
data-extra-info="Got lifetime access, but would be nice to finish it soon!"
class="card"
draggable="true"
>
<h2>Finish the Course</h2>
<p>Finish the course within the next two weeks.</p>
<button class="alt">More Info</button>
<button>Finish</button>
</li>
<li
id="p2"
data-extra-info="Not really a business topic but still important."
class="card"
draggable="true"
>
<h2>Buy Groceries</h2>
<p>Don't forget to pick up groceries today.</p>
<button class="alt">More Info</button>
<button>Finish</button>
</li>
</ul>
</section>
<section id="finished-projects">
<header>
<h2>Finished Projects</h2>
</header>
<ul>
<li
id="p3"
data-extra-info="Super important conference! Fictional but still!"
class="card"
draggable="true"
>
<h2>Book Hotel</h2>
<p>
Academind conference takes place in December, don't forget to book a
hotel.
</p>
<button class="alt">More Info</button>
<button>Activate</button>
</li>
</ul>
</section>
<footer>
<button id="im-done-btn">I'm Done!</button>
</footer>
</body>
</html>
And here's the CSS snippet if relevant:
* {
box-sizing: border-box;
}
html {
font-family: sans-serif;
}
body {
margin: 0;
}
#main-header {
width: 100%;
height: 6rem;
display: flex;
justify-content: center;
align-items: center;
background: #ff0062;
}
#main-header h1 {
color: white;
margin: 0;
}
footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin: 1rem 0;
}
section {
margin: 1rem auto;
width: 40rem;
max-width: 90%;
}
section ul {
padding: 1rem;
max-height: 20rem;
overflow: scroll;
}
section > h2 {
color: white;
margin: 0;
}
button {
font: inherit;
background: #ff0062;
color: white;
border: 1px solid #ff0062;
padding: 0.5rem 1.5rem;
cursor: pointer;
}
button.alt {
background: white;
color: #ff0062;
}
button:focus {
outline: none;
}
button:hover,
button:active {
background: #ff2579;
border-color: #ff2579;
color: white;
}
.card {
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
padding: 1rem;
background: white;
}
.droppable {
background: #ffe0ec
}
#active-projects {
border: 1px solid #870099;
}
#active-projects > header {
background: #870099;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#active-projects header h2 {
color: white;
margin: 0;
}
#finished-projects {
border: 1px solid #535353;
}
#finished-projects > header {
background: #535353;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#finished-projects header h2 {
color: white;
margin: 0;
}
The problem can be solved through CSS, and you can remove the following from your Javascript code:
tooltipElement.style.position = 'absolute';
tooltipElement.style.left = x + 'px';
tooltipElement.style.top = y + 'px';
Instead of that, change your CSS, by adding these:
li.card {
position: relative;
}
div.card {
position: absolute;
top: 0; /* or some other value, experiment until you find the one which works for you */
left: 0; /* same as above */
z-index: 1;
}
For this to work, the div.card element, which you are creating in your Javascript code, needs to be a child element of the appropriate li.card element. I'm also (perhaps erroneously) assuming that your original code is similar to the one I found earlier, and posted in the comment to your question, since I couldn't get your original code to run.
Your position:absolute wasn't working originally, because the position of the li.card parent was not explicitly set, which was defaulting it to static.
EDIT Forgot to mention the z-index bit. This is needed because your info box wouldn't be fully visible (it would be under the next li.card element).
I am a beginner and I am using several tutorials to learn and create a project. I am using the NASA APOD API to display images. However, I want to display the image when clicked within a modal. For some reason the image when clicked is displaying the modal, but without the image. How do I click on the image and display it within the modal.
const resultsNav = document.getElementById("resultsNav");
const favoritesNav = document.getElementById("favoritesNav");
const imagesContainer = document.querySelector(".images-container");
const saveConfirmed = document.querySelector(".save-confirmed");
const loader = document.querySelector(".loader");
// NASA API
const count = 3;
const apiKey = 'DEMO_KEY';
const apiUrl = `https://api.nasa.gov/planetary/apod?api_key=${apiKey}&count=${count}`;
let resultsArray = [];
let favorites = {};
// Show Content
function showContent(page) {
window.scrollTo({ top: 0, behavior: "instant" });
if (page === "results") {
resultsNav.classList.remove("hidden");
favoritesNav.classList.add("hidden");
} else {
resultsNav.classList.add("hidden");
favoritesNav.classList.remove("hidden");
}
loader.classList.add("hidden");
}
// Create DOM Nodes
function createDOMNodes(page) {
const currentArray =
page === "results" ? resultsArray : Object.values(favorites);
currentArray.forEach((result) => {
// Card Container
const card = document.createElement("div");
card.classList.add("card");
// Link that wraps the image
const link = document.createElement("a");
// link.href = result.hdurl; -- full size image display when clicked
// Get the modal
var modal = document.getElementById("myModal");
// Get the image and insert it inside the modal - use its "alt" text as a caption
var img = document.getElementById("myImg");
var modalImg = document.getElementById("img01");
var captionText = document.getElementById("caption");
img.onclick = function(){
modal.style.display = "block";
modalImg.src = this.src;
captionText.innerHTML = this.alt;
}
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.display = "none";
}
// Image
const image = document.createElement("img");
image.src = result.url;
image.alt = "NASA Picture of the Day";
image.loading = "lazy";
image.classList.add("card-img-top");
// Card Body
const cardBody = document.createElement("div");
cardBody.classList.add("card-body");
// Card Title
const cardTitle = document.createElement("h5");
cardTitle.classList.add("card-title");
cardTitle.textContent = result.title;
// Save Text
const saveText = document.createElement("p");
saveText.classList.add("clickable");
if (page === "results") {
saveText.textContent = "Add To Favorites";
saveText.setAttribute("onclick", `saveFavorite('${result.url}')`);
} else {
saveText.textContent = "Remove Favorite";
saveText.setAttribute("onclick", `removeFavorite('${result.url}')`);
}
// Card Text
const cardText = document.createElement("p");
cardText.textContent = result.explanation;
// Footer Conatiner
const footer = document.createElement("small");
footer.classList.add("text-muted");
// Date
const date = document.createElement("strong");
date.textContent = result.date;
// Copyright
const copyrightResult =
result.copyright === undefined ? "" : result.copyright;
const copyright = document.createElement("span");
copyright.textContent = ` ${copyrightResult}`;
// Append everything together
footer.append(date, copyright);
cardBody.append(cardTitle, saveText, cardText, footer); //hide to make image display
link.appendChild(image);
card.append(link); // hide cardBody
// Append to image container
imagesContainer.appendChild(card);
});
}
// Update the DOM
function updateDOM(page) {
// Get favorites from local storage
if (localStorage.getItem("nasaFavorites")) {
favorites = JSON.parse(localStorage.getItem("nasaFavorites"));
}
imagesContainer.textContent = "";
createDOMNodes(page);
showContent(page);
}
// Get 10 images from NASA API
async function getNasaPictures() {
// Show Loader
loader.classList.remove("hidden");
try {
const response = await fetch(apiUrl);
resultsArray = await response.json();
updateDOM("results");
} catch (error) {
// Catch Error Here
}
}
// Add result to favorites
function saveFavorite(itemUrl) {
// Loop through the results array to select favorite
resultsArray.forEach((item) => {
if (item.url.includes(itemUrl) && !favorites[itemUrl]) {
favorites[itemUrl] = item;
// Show save confirmation for 2 seconds
saveConfirmed.hidden = false;
setTimeout(() => {
saveConfirmed.hidden = true;
}, 2000);
// Set Favorites in Local Storage
localStorage.setItem("nasaFavorites", JSON.stringify(favorites));
}
});
}
// Remove item from favorites
function removeFavorite(itemUrl) {
if (favorites[itemUrl]) {
delete favorites[itemUrl];
localStorage.setItem("nasaFavorites", JSON.stringify(favorites));
updateDOM("favorites");
}
}
// On Load
getNasaPictures();
.container {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 3% 1fr 0.1fr;
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas:
"header"
"content"
}
.hero {
grid-area: hero;
background-color: blue;
}
.content {
grid-area: content;
background-color: orange;
align-self: center;
justify-self: center;
}
html {
box-sizing: border-box;
}
body {
margin: 0;
background: whitesmoke;
overflow-x: hidden;
font-family: Verdana, sans-serif;
font-size: 1rem;
line-height: 1.8rem;
}
.loader {
position: fixed;
z-index: 40;
background: whitesmoke;
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
/* Navigation */
.navigation-container {
position: fixed;
top: 0;
}
.navigation-items {
display: flex;
justify-content: center;
}
.background {
background: whitesmoke;
position: fixed;
right: 0;
width: 100%;
height: 60px;
z-index: -1;
}
.clickable {
color: #0b3d91;
cursor: pointer;
user-select: none;
}
.clickable:hover {
color: #fc3d21;
}
/* Images Container */
.images-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.card {
margin: 10px 10px 10px;
width: 300px;
height: 300px;
}
.card-img-top {
width: 300px;
height: 300px;
}
.card-body {
padding: 20px;
}
.card-title {
margin: 10px auto;
font-size: 24px;
}
/* Save Confirmation */
.save-confirmed {
background: white;
padding: 8px 16px;
border-radius: 5px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
position: fixed;
bottom: 25px;
right: 50px;
z-index: 500;
}
/* Hidden */
.hidden {
display: none;
}
.brand {
float: right;
}
.fave {
margin-right: 50%;
}
#myImg {
border-radius: 5px;
cursor: pointer;
transition: 0.3s;
}
/* #myImg:hover {opacity: 0.7;} */
/* The Modal (background) */
.modal {
display: none;
/* Hidden by default */
position: fixed;
/* Stay in place */
z-index: 1;
/* Sit on top */
padding-top: 100px;
/* Location of the box */
left: 0;
top: 0;
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.9);
/* Black w/ opacity */
}
/* Modal Content (image) */
.modal-content {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
}
/* Caption of Modal Image */
#caption {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
text-align: center;
color: #ccc;
padding: 10px 0;
height: 150px;
}
/* The Close Button */
.close {
position: absolute;
top: 15px;
right: 35px;
color: #f1f1f1;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
}
.close:hover,
.close:focus {
color: #bbb;
text-decoration: none;
cursor: pointer;
}
/* 100% Image Width on Smaller Screens */
#media only screen and (max-width: 700px) {
.modal-content {
width: 100%;
}
}
<!-- Loader -->
<div class="loader hidden">
<img src="rocket.svg" alt="Rocket Icon" />
</div>
<!-- Container -->
<div class="container">
<div class="header">
<div class="navigation-container">
<span class="background"></span>
<!-- Results Nav -->
<span class="navigation-items" id="resultsNav">
</span>
<!-- Favorites Nav -->
<span class="navigation-items hidden" id="favoritesNav">
</span>
</div>
</div>
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="column">
<div id="myImg" alt="Snow" class="images-container"></div>
</div>
</div>
</div>
</div>
<!-- The Modal -->
<div id="myModal" class="modal">
<span class="close">×</span>
<img class="modal-content" id="img01">
<div id="caption"></div>
</div>
</div>
In your img.onclick function/event listener (at line 51 of your JS), your this points to the parent instead of the image itself.
For a quick fix, try replacing your this with event.target (Basically, event.target.src in place of this.src and event.target.alt in place of this.alt at lines 56 and 57 of your current JS Fiddle)
how can i create dialog effect like google keep.i tried to debug the css but without any success.
is there code example out there ?
i see that they using hidden position fixed modal that triggering on click but how they calculate the position.
This is what i could make out of your question(pure JS and CSS).
Below is the code
var example_note = document.getElementsByClassName('example_note')[0];
var close_btn = document.getElementById('close_btn');
example_note.onclick = function() {
document.getElementsByClassName('background_change')[0].style.display = "block";
document.getElementsByClassName('display_block')[0].style.display = "block";
example_note.style.display="none";
}
close_btn.onclick = function() {
document.getElementsByClassName('background_change')[0].style.display = "none";
document.getElementsByClassName('display_block')[0].style.display = "none";
example_note.style.display="block";
}
* {
margin: 0px;
padding: 0px;
font-family: 'arial';
}
.example_note {
position: absolute;
width: 250px;
margin-top: 10%;
margin-left: 15%;
box-shadow: -1px 1px 10px 3px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: -1px 1px 10px 3px rgba(0, 0, 0, 0.2);
-moz-box-shadow: -1px 1px 10px 3px rgba(0, 0, 0, 0.2);
padding: 30px;
border-radius: 15px;
background-color: white;
}
.example_note h1 {
font-size: 23px;
}
.display_block {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 450px;
background-color: white;
padding: 30px;
border-radius: 15px;
transform-origin: 0 25%;
animation: show_block 0.2s 1;
}
#keyframes show_block {
from {
transform: translate(-50%, -50%)scale(0);
}
to {
transform: translate(-50%, -50%)scale(1);
}
}
input[type="text"] {
width: 390px;
padding: 10px;
border: none;
}
input[type="text"]:focus {
outline: none;
}
button {
float: right;
background-color: white;
padding: 10px 20px 10px 20px;
border-radius: 8px;
border: none;
font-size: 17px;
transition: 0.4s;
font-weight: bold;
outline: none;
}
button:hover {
background-color: #E3E3E3;
}
.background_change {
display: none;
height: 100%;
width: 100%;
position: absolute;
background-color: black;
opacity: 0.6;
animation: show_back 0.5s 1;
}
#keyframes show_back {
from {
opacity: 0;
}
to {
opacity: 0.6;
}
}
<div class="example_note">
<h1>Example Note</h1>
</div>
<div class="background_change"></div>
<div class="display_block">
<input type="text" name="title" placeholder="Title" style="font-size: 25px;">
<br>
<input type="text" name="name" value="Example Note" style="font-size: 15px; font-weight: bold;">
<br>
<button id="close_btn">close</button>
</div>
This answer is plain JavaScript and CSS (no libraries), and produces the following effect:
A full working example is contained in the following snippet (best previewed full screen):
function openModal(noteEl, modalEl, modalContainerEl) {
// Compute and apply the transform to deform the modal to cover the note with a transition to make it animate
const transform = computeTransform(noteEl);
modalEl.style.transform = transform;
modalEl.style.transition = 'transform 250ms';
// Setup the modal background animate in too
modalContainerEl.style.backgroundColor = 'transparent';
modalContainerEl.style.transition = 'background-color 250ms';
// Show the modal
modalContainerEl.classList.add('modal-container--open');
// Put the rest in a setTimeout to allow the styles applied above to take
// affect and render before we overwrite them with new ones below
setTimeout(function () {
// Remove the transform to allow the modal to return to it's natural shape and position
modalEl.style.transform = 'none';
modalContainerEl.style.backgroundColor = 'rgba(33, 33, 33, 0.5)';
}, 0)
}
function computeTransform(noteEl) {
// Modal positions here are hardcoded to match styles set in CSS
const modalTop = 150;
const modalLeft = (document.body.offsetWidth / 2) - 300;
const modalWidth = 600;
const modalHeight = 150;
// Get note div's position relative to the viewport
const notePosition = noteEl.getBoundingClientRect();
// Compute a CSS transform that moves the modal to match the note's position
const translateX = notePosition.left - modalLeft;
const translateY = notePosition.top - modalTop;
const scaleX = notePosition.width / modalWidth;
const scaleY = notePosition.height / modalHeight;
return `translateX(${translateX}px) translateY(${translateY}px) scaleX(${scaleX}) scaleY(${scaleY})`;
}
// Handle click events using event delegation
document.addEventListener('click', function (event) {
// Handle click events on note elements (open modal)
if (event.target.className === 'note') {
// Get a reference
const modalContainerEl = document.querySelector('.modal-container');
const modalEl = document.querySelector('.modal');
openModal(event.target, modalEl, modalContainerEl);
}
// Handle click event on modal background element (close modal)
if (event.target.classList.contains('modal-container')) {
event.target.classList.remove('modal-container--open');
}
})
body {
display: flex;
flex-wrap: wrap;
width: 100%;
color: #333;
}
.note {
flex: 0 0 200px;
width: 200px;
height: 200px;
border: 1px solid #CCC;
margin: 12px;
border-radius: 10px;
}
.modal-container {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(33, 33, 33, 0.5);
}
.modal-container--open {
display: block;
}
.modal {
position: absolute;
top: 150px;
left: 50%;
margin-left: -300px;
width: 600px;
height: 150px;
transform-origin: top left;
will-change: transform; /* makes the animation run smoother */
background-color: #EEE;
border-radius: 10px;
}
<!DOCTYPE html>
<html>
<head>
<style type="text/css" />
</style>
<script type="text/javascript">
</script>
</head>
<body>
<div class="note">1</div>
<div class="note">2</div>
<div class="note">3</div>
<div class="note">4</div>
<div class="note">5</div>
<div class="note">6</div>
<div class="note">7</div>
<div class="note">8</div>
<div class="note">9</div>
<div class="note">10</div>
<div class="note">11</div>
<div class="note">12</div>
<div class="note">13</div>
<div class="note">14</div>
<div class="note">15</div>
<div class="note">16</div>
<div class="note">17</div>
<div class="note">18</div>
<div class="note">19</div>
<div class="note">20</div>
<div class="modal-container">
<div class="modal">
Modal
</div>
</div>
</body>
</html>
The trick is to apply a CSS transform to the modal in order to deform into the shape/position of the clicked note before showing the modal. We can then remove the transform and use a CSS transition to get it to smoothly animate into it's natural shape/position.
We calculate the transform as follows:
function computeTransform(noteEl) {
// Modal positions here are hardcoded to match styles set in CSS
const modalTop = 150;
const modalLeft = (document.body.offsetWidth / 2) - 300;
const modalWidth = 600;
const modalHeight = 150;
// Get note div's position relative to the viewport
const notePosition = noteEl.getBoundingClientRect();
// Compute a CSS transform that moves the modal to match the note's position
const translateX = notePosition.left - modalLeft;
const translateY = notePosition.top - modalTop;
const scaleX = notePosition.width / modalWidth;
const scaleY = notePosition.height / modalHeight;
return `translateX(${translateX}px) translateY(${translateY}px) scaleX(${scaleX}) scaleY(${scaleY})`;
}
And we apply it as follows:
function openModal(noteEl, modalEl, modalContainerEl) {
// Compute and apply the transform to deform the modal to cover the note with a transition to make it animate
const transform = computeTransform(noteEl);
modalEl.style.transform = transform;
modalEl.style.transition = 'transform 250ms';
// Setup the modal background animate in too
modalContainerEl.style.backgroundColor = 'transparent';
modalContainerEl.style.transition = 'background-color 250ms';
// Show the modal
modalContainerEl.classList.add('modal-container--open');
// Put the rest in a setTimeout to allow the styles applied above to take
// affect and render before we overwrite them with new ones below
setTimeout(function () {
// Remove the transform to allow the modal to return to it's natural shape and position
modalEl.style.transform = 'none';
modalContainerEl.style.backgroundColor = 'rgba(33, 33, 33, 0.5)';
}, 0)
}
Note the setTimeout between applying and removing the transform. This is important as otherwise the transform will never actually be applied.
See the snippet for the full details, but also of note: the transform-origin: top left; style on the modal is important to make the transform computation work.
I did like the example above, wich worked perfectly. I added opacity to de card selected to remove erase the card from the screen when it is clicked
<style type="text/css">
body {
display: flex;
flex-wrap: wrap;
width: 100%;
color: #333;
}
.note {
flex: 0 0 200px;
width: 200px;
height: 200px;
border: 1px solid #CCC;
margin: 12px;
border-radius: 10px;
}
.modal-container {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(33, 33, 33, 0.5);
}
.modal-container--open {
display: block;
}
.modal {
position: absolute;
top: 150px;
left: 50%;
margin-left: -300px;
width: 600px;
height: 150px;
transform-origin: top left;
will-change: transform;
/* makes the animation run smoother */
background-color: #EEE;
border-radius: 10px;
}
</style>
<script type="text/javascript">
function openModal(noteEl, modalEl, modalContainerEl) {
// Compute and apply the transform to deform the modal to cover the note with a transition to make it animate
const transform = computeTransform(noteEl);
modalEl.style.transform = transform;
modalEl.style.transition = 'transform 250ms';
// Setup the modal background animate in too
modalContainerEl.style.backgroundColor = 'transparent';
modalContainerEl.style.transition = 'background-color 250ms';
// Show the modal
modalContainerEl.classList.add('modal-container--open');
noteEl.style.opacity = 0;
// Put the rest in a setTimeout to allow the styles applied above to take
// affect and render before we overwrite them with new ones below
setTimeout(function () {
// Remove the transform to allow the modal to return to it's natural shape and position
modalEl.style.transform = 'none';
modalContainerEl.style.backgroundColor = 'rgba(33, 33, 33, 0.5)';
}, 0)
}
function computeTransform(noteEl) {
// Modal positions here are hardcoded to match styles set in CSS
const modalTop = 150;
const modalLeft = (document.body.offsetWidth / 2) - 300;
const modalWidth = 600;
const modalHeight = 150;
// Get note div's position relative to the viewport
const notePosition = noteEl.getBoundingClientRect();
// Compute a CSS transform that moves the modal to match the note's position
const translateX = notePosition.left - modalLeft;
const translateY = notePosition.top - modalTop;
const scaleX = notePosition.width / modalWidth;
const scaleY = notePosition.height / modalHeight;
return `translateX(${translateX}px) translateY(${translateY}px) scaleX(${scaleX}) scaleY(${scaleY})`;
}
// Handle click events using event delegation
let cardSelected;
document.addEventListener('click', function (event) {
// Handle click events on note elements (open modal)
if (event.target.className === 'note') {
// Get a reference
cardSelected = event.target
const modalContainerEl = document.querySelector('.modal-container');
const modalEl = document.querySelector('.modal');
openModal(event.target, modalEl, modalContainerEl);
}
// Handle click event on modal background element (close modal)
if (event.target.classList.contains('modal-container')) {
event.target.classList.remove('modal-container--open');
cardSelected.style.opacity = 1;
}
})
</script>
<div class="note">1</div>
<div class="note">2</div>
<div class="note">3</div>
<div class="note">4</div>
<div class="note">5</div>
<div class="note">6</div>
<div class="note">7</div>
<div class="note">8</div>
<div class="note">9</div>
<div class="note">10</div>
<div class="note">11</div>
<div class="note">12</div>
<div class="note">13</div>
<div class="note">14</div>
<div class="note">15</div>
<div class="note">16</div>
<div class="note">17</div>
<div class="note">18</div>
<div class="note">19</div>
<div class="note">20</div>
<div class="modal-container">
<div class="modal">
Modal
</div>
</div>
To create a modal, you can use a library called Swal. Swal, however does not look like keep's popups, so I've restyled it below.
script links you must reference to:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#sweetalert2/theme-material-ui/material-ui.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2#10"></script>. You may also wish to visit Google Fonts and pick a nice font-family. Test this pop-up by clicking run this snippet.
const MySwal = Swal.mixin({
//background: "rgb(10,10,10)",
background: "white",
showCloseButton: true,
backdrop: "rgba(0,0,0,0.7)",
showClass: {
popup: "animate__animated animate__fadeInDown med"
},
hideClass: {
popup: "animate__animated animate__fadeOutUp fast"
},
width: "95vw"
});
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.js" integrity="sha512-HBD0cOZJYcymSn0H0CnN3VBhQLdiH8imucm16ZQ792TT2n48u6nmX+T7hZTCwmzIrgMt76x4rHhR7KkZqhIGxA==" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2#10"></script>
<script src="alpha.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
<link rel="shortcut icon" href="favicon.png" id="iconshort">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#sweetalert2/theme-material-ui/material-ui.css">
<script src='https://kit.fontawesome.com/a076d05399.js'></script>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#300&display=swap" rel="stylesheet">
</head>
<body>
<button onclick="MySwal.fire('Title','Content')">LAUNCH POP-UP</button>
</body>
const MySwal = Swal.mixin({
//background: "rgb(10,10,10)",
background: "white",
showCloseButton: true,
backdrop: "rgba(0,0,0,0.7)",
showClass: {
popup: "animate__animated animate__fadeInDown med"
},
hideClass: {
popup: "animate__animated animate__fadeOutUp fast"
},
width: "95vw",
willOpen: function() {
open_audio.play();
},
willClose: function() {
confirm_audio.play();
}
});
I've tried to look for a solution for this but have failed miserably. It's my first ever time using JS (I'm trying to learn) so the possibility of my just not understanding the answers in the search results properly is quite high - sorry about that.
I am wanting a JS carousel, generated from an array, with Prev/Next buttons (ideally responsive etc but that'll come at a later stage), preferably with captions underneath. I can get the carousel to work but I end up getting a text link when I click on either Prev or Next. And I've no idea how to add the caption array underneath (I've taken out the JS for the captions for now because it was messing everything else up even further).
Relevant HTML:
<body onload="changePilt()">
<span id="prev" class="arrow">❮</span>
<div class="karussell" id="karussell">
<img class="karu" name="esislaid">
</div>
<span id="next" class="arrow">❯</span>
<div class="caption">
<h3 name="esikiri"></h3>
</div>
</body>
CSS, just in case:
.karussell {
position: relative;
width: 100%;
max-height: 600px;
overflow: hidden;
}
.arrow {
cursor: pointer;
position: absolute;
top: 40%;
width: auto;
color: #00A7E0;
margin-top: -22px;
padding: 16px;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
}
#next {
right: 0;
border-radius: 3px 0 0 3px;
}
#prev {
left: 0;
}
.arrow:hover {
background-color: rgba(0,0,0,0.8);
}
.caption {
text-align: center;
color: #00A7E0;
padding: 2px 16px;
}
.karu {
max-width: 75%;
}
#media (max-width:767px){.karu{max-width: 95%;}}
And finally, the dreaded JS:
var i = 0;
var s = 0;
var esileht = [];
var aeg = 5000;
//Image List
esileht[0] = 'img/tooted/raamat/graafvanalinn2016.jpg';
esileht[1] = 'img/tooted/kaart/kaart_taskus_esipool.jpg';
esileht[2] = 'img/tooted/kaart/graafkaart_esikylg.jpg';
//Change Image
function changePilt (){
document.esislaid.src = esileht[i];
if(i < esileht.length -1){
i++;
} else {
i = 0;
}
setTimeout("changePilt()", aeg);
}
document.onload = function() {
}
// Left and Right arrows
//J2rgmine
function jargmine(){
s = s + 1;
s = s % esileht.length;
return esileht [s];
}
//Eelmine
function eelmine(){
if (s === 0) {
s = esileht.length;
}
s = s -1;
return esileht[s];
}
document.getElementById('prev').addEventListener('click', function (e){
document.getElementById('karussell').innerHTML = eelmine();
}
);
document.getElementById('next').addEventListener('click', function (e) {
document.getElementById('karussell').innerHTML = jargmine();
}
);
I'm sure the solution is dreadfully obvious, I just cannot seem to be able to figure it out...
instead of innerHTML change src attribute of image
document.querySelector('#karussell img').src = eelmine();
And
document.querySelector('#karussell img').src = jargmine();
I was trying to move the divs (here it's question number) based on the prev and next button. So that the selected question is always visible on screen.
Here is the demo : http://jsfiddle.net/arunslb123/trxe4n3u/12/
Screen :
click and question number and click prev or next button to understand my issue.
My code :
$("#next")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': scrollTo
}, 800);
});
});
$("#prev")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': -scrollTo
}, 800);
});
});
Using scrollLeft is a bit tricky. I did a small redo of your use-case based on positioning and then moving it based on left of the container. The tricky part is to reliably calculate the negative position when scrolled to the extreme right. Also, need to take into account the widths and margins.
Check the below snippet:
var $wrap = $("#numWrap"), $strip = $("#strip"),
$leftArrow = $(".wrapper > .arrows").first(),
wrapWidth = $wrap.width() + $leftArrow.width(),
margin = 10;
fill(20); select($(".numberItem").first());
$strip.on("click", ".numberItem", function() { select($(this)); });
function select($elem) {
$(".numberItem").removeClass("selected");
$elem.addClass("visited").addClass("selected");
focus($elem[0]);
}
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
$(".wrapper").on("click", "a.arrow", function() {
var stripPos = $strip.position();
if (this.id == "lft") {
$strip.css({"left": stripPos.left + (wrapWidth / 2)});
} else {
$strip.css({"left": stripPos.left - (wrapWidth / 2)});
}
});
$(".controls").on("click", "a.arrow", function() {
var $sel = $(".selected"), numPos, $sel, elemWidth;
$elem = $sel.length > 0 ? $sel.first() : $(".numberItem").first();
if (this.id == "lft") {
$sel = $elem.prev().length > 0 ? $elem.prev() : $elem;
select($sel);
} else {
$sel = $elem.next().length > 0 ? $elem.next() : $elem;
select($sel);
}
numPos = $sel.offset(); elemWidth = $sel.width() + margin;
numRight = numPos.left + elemWidth;
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
});
function fill(num){
for (var i = 1; i <= num; i++) {
var $d = $("<a href='#' class='numberItem'>" + i + "</a>");
$strip.append($d);
}
}
* { box-sizing: border-box; padding: 0; margin: 0; font-family: sans-serif; }
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative;
}
div.strip {
position: absolute; left: 0px;
width: auto; white-space: nowrap;
transition: left 1s;
}
a.numberItem {
display: inline-block; text-align: center; margin: 0px 8px;
background-color: #fff; border-radius: 50%; width: 48px; height: 48px;
font-size: 1.2em; line-height: 48px; text-decoration: none;
}
a.numberItem.visited { background-color: #fff; color: #000; border: 2px solid #01aebc; }
a.numberItem.selected { background-color: #01aebc; color: #fff; }
div.controls { clear: both; }
div.controls > div.arrows { width: auto; margin: 0 12px; }
a, a:focus, a:active, a:link, a:visited {
display: inline-block;
text-decoration: none; font-weight: 600;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈</a>
</div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">〉</a>
</div>
</div>
<div class="controls">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈 Previous</a>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">Next 〉</a>
</div>
<div>
Explanation:
Using absolute positioning on the number container, which is nested to get 100% width.
Markup:
<div class="wrapper">
<div class="arrows"><a id="lft" class="arrow" href="#">〈</a></div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div> <!-- nesting here -->
</div>
<div class="arrows"><a id="rgt" class="arrow" href="#">〉</a></div>
</div>
CSS:
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative; /* relatively positioned */
}
div.strip {
position: absolute; left: 0px; /* absolutely positioned */
width: auto; white-space: nowrap;
transition: left 1s; /* instead of jquery animate */
}
With this structure, we can now use left to control the scrolling.
For partially obscured numbers, try to gently focus-in (nudge into view) a number which is partially obscured. This can be done by checking the position relative to parent and adding the width/margin to it and also accounting for width of the left arrow (it might peep thru).
Javascript:
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
// if it is towards right side, nudge it back inside
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
// if it is towards left side, nudge it back inside
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
Once the user has scrolled the list too far and then tries to click on previous / next buttons to select a question, then we need to move the entire container upto the selected number. We can easily do this by multiplying the question number with element width and then changing the left in positive (if towards right) or in negative (if towards left).
Javascript:
// if left of element is more than the width of parent
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
// if right of element is less than 0 i.e. starting position
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
Here is a fiddle to play with: http://jsfiddle.net/abhitalks/aw166qhx/
You will need to further adapt it to your use-case, but you get the idea.