Ok... I'm struggling here. I'm particularly new at JavaScript and am trying to fetch data from the Art Institute of Chicago's API. My goal is to display the information for a particular piece of art in a modal window after clicking on the relevant artwork. I've gotten the CSS and HTML down, I think. Everything displays properly as far as the images and titles and such. However, when I click the image to open the modal, all I can get to display is the Art itself.
I've found a similar question on here and tried some of its code, but their solution didn't quite get me to where I need to be.
Here's my HTML:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css">
<script src="index.js" defer></script>
<title>Art Institute of Chicago API</title>
</head>
<body>
<h1>From the Art Institute of Chicago:<br>A Sampling of Monet, Van Gogh, and Hokusai</h1>
<div class="container">
<!-- ARTic Banner -->
<div class="gallery-container w-4 h-3">
<div class="gallery-item">
<div class="image banner" onclick="openInNewTab('https:/\/www.artic.edu');">
<img src="/images/bannerArtIC.jpg"
alt="Art Institute of Chicago banner">
</div>
<div class="text">
The Art Institute of Chicago
</div>
</div>
</div>
<!-- Fig 1 - Monet: Water Lilies -->
<div class="gallery-container w-2 h-3">
<div class="gallery-item">
<div class="image" id="16568">
<img src="images/monetWaterLilies_1933.1157.jpg"
alt="Painting of a pond seen up close spotted with thickly
painted pink and white water lilies and a shadow across the
top third of the picture."
onclick="openModal(event); event.stopPropagation(); getArtwork(this.id, event)">
</div>
<div class="text">
Claude Monet:<br>Water Lilies
</div>
</div>
</div>
<!-- Fig 2 - Van Gogh: Self Portrait -->
<div class="gallery-container w-2 h-6">
<div class="gallery-item">
<div class="image" id="80607">
<img src="images/vanGoghSelfPortrait_1954.326.jpg"
alt="Painting of a red-haired, bearded man with light skin,
painted in short brushstrokes and multicolored dots. The
background is likewise a mass of small, closely spaced colored
dots, these in green, blue, and red-orange."
onclick="openModal(event); event.stopPropagation(); getArtwork(this.id, event)">
</div>
<div class="text">
Vincent Van Gogh:<br>Self Portrait
</div>
</div>
</div>
<!-- Fig 3 - Monet: Water Lily Pond -->
<div class="gallery-container w-4 h-3">
<div class="gallery-item">
<div class="image" id="87088">
<img src="images/monetWaterLilyPond_1933.441.jpg"
alt="Painting of a small pond dense with pink water lilies,
their roots visible through the water, a railed footbridge
arching over the pond and lush, dark green folliage surrounding
it."
onclick="openModal(event); event.stopPropagation(); getArtwork(this.id, event)">
</div>
<div class="text">
Claude Monet:<br>Water Lily Pond
</div>
</div>
</div>
<!-- Fig 4 - Van Gogh: The Poet's Garden -->
<div class="gallery-container w-2 h-3">
<div class="gallery-item">
<div class="image" id="14586">
<img src="images/vanGoghThePoetsGarden_1933.433.jpg"
alt="Lush tall grasses with small white flowers foreground a
grove of bushy trees of varying types and heights, their leaves
ranging from deep green to golden. Beneath a dense and heavy
yellow sky, a small blue triangle suggesting a mountain peak
crests above the treeline at far left."
onclick="openModal(event); event.stopPropagation(); getArtwork(this.id, event)">
</div>
<div class="text">
Vincent Van Gogh:<br>The Poet's Garden
</div>
</div>
</div>
<!-- Fig 5 - Hokusai: Under the Wave Off Kanagawa -->
<div class="gallery-container w-4 h-3">
<div class="gallery-item">
<div class="image" id="24645">
<img src="images/hokusaiUnderTheWaveOffKanagawa_1925.3245.jpg"
alt="A crashing wave looms over two small ships, Mount Fuji
in the background."
onclick="openModal(event); event.stopPropagation(); getArtwork(this.id, event)">
</div>
<div class="text">
Katsushika Hokusai:<br>Under the Wave Off Kanagawa
</div>
</div>
</div>
<!-- Fig 6 - Hokusai: Chrysanthemums and Bee -->
<div class="gallery-container w-2 h-6">
<div class="gallery-item">
<div class="image" id="25110">
<img src="images/hokusaiChrysanthemumsAndBee_1925.3373.jpg"
alt="A color woodblock print of pink, orange and yellow
chrysanthemums. In the upper rihgt a tiny bee flies above the
flowers. Signature and seals in the lower left corner."
onclick="openModal(event); event.stopPropagation(); getArtwork(this.id, event)">
</div>
<div class="text">
Katsushika Hokusai:<br>Chrysanthemums and Bee
</div>
</div>
</div>
<!-- div for modal -->
<div id="myModal" class="modal">
<span class="closebtn" onclick="closeModal(event)">×</span>
<img class="modal-content" id="expandedImg">
<div id="caption">
</div>
</div>
</div>
</body>
</html>
And here's my JS (with additions from answers found on stack overflow):
// function to open the AIC homepage in new tab
function openInNewTab(url) {
var win = window.open(url, 'blank');
win.focus();
}
// function to open each image as a modal once it is clicked
function openModal(event) {
var modal = document.getElementById("myModal");
modal.style.display = "block";
document.getElementById("expandedImg").setAttribute('src', event.target.getAttribute("src"));
}
// function to close the image modal
function closeModal(event) {
const modal = document.getElementById("myModal");
modal.style.display = "none";
}
// // // // // // // // // // // // // // // // // // // // // // // // // // //
// //
// functions and methods to pull data for art pieces //
// let art;
let showArtInfo;
/**
* #param art_index
* #param info_index
*
* function to pull the info on the art from AIC
*/
async function clickedEvent(art_index, info_index) { // is the info_index actually necessary here if it's not used again? is that for the API's benefit?
// Get id of artwork
let elem = document.getElementsByTagName('img')[art_index];
let id = elem.attributes[2].value;
let headers = new Headers([
['Content-Type', 'application/json'],
['Accept', 'application/json']
]);
let request = new Request(
`https://api.artic.edu/api/v1/artworks${id}?fields=id,title,artist_display,date_display`, {
method: 'GET',
headers: headers
});
let result = await fetch(request);
let response = await result.json();
console.log(response)
if (showArtInfo) {
stopShow();
} else {
let title = response.data.title;
let artist = response.data['artist_display'];
let date = response.data['date_display'];
let div = document.getElementById('caption');
div.innerHTML = `<br> Title: ${title}<br> Artist: ${artist}<br> Date Display: ${date}`;
elem.parentElement.appendChild(div);
};
}
/**
* #param id // art id
* #param event // 'onClick' event
*
* function to display the pulled info of the artpiece when clicked on
*/
function getArtwork(id, event) {
switch(id) {
case '16568': { // Water Lilies by Claude Monet, id# 16568
event.stopPropagation();
clickedEvent(0, 0)
break;
}
case '80607': { // Self Portrait by Vincent Van Gogh, id# 80607
event.stopPropagation();
clickedEvent(1, 0)
break;
}
case '87088': { // Water Lily Pond by Claude Monet, id# 87088
event.stopPropagation();
clickedEvent(2, 0)
break;
}
case '14586': { // The Poet's Garden by Vincent Van Gogh, id# 14586
event.stopPropagation();
clickedEvent(3, 0)
break;
}
case '24645': { // Under the Wave Off Kanagawa by Katsushika Hokusai, id# 24645
event.stopPropagation();
clickedEvent(4, 0)
break;
}
case '25110': { // Chrysanthemums and Bee by Katsushika Hokusai, id# 25110
event.stopPropagation();
clickedEvent(5, 0)
break;
}
}
}
And lastly, my CSS (in case you want to fully replicate my page to see what I'm going for in real time):
body {
margin: 50px;
padding: 0;
text-align: center;
background-color: #292C33;
}
h1 {
margin-bottom: 50px;
padding: 20px;
outline: 5px solid white;
color: white;
border-radius: 5px;
background-color: #b60235;
}
.container {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-auto-rows: 100px 100px;
grid-gap: 18px;
grid-auto-flow: dense;
}
.gallery-item {
width: 100%;
height: 100%;
position: relative;
}
.gallery-item .image {
width: 100%;
height: 100%;
overflow: hidden;
border: 2px solid white;
border-radius: 5px;
}
.gallery-item .image img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: 50% 50%;
cursor: pointer;
transition: .66s ease-in-out;
border-radius: 5px;
}
.gallery-item:hover .image img {
transform: scale(1.12);
}
.gallery-item .text {
opacity: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 30px;
pointer-events: none;
z-index: 4;
-webkit-backdrop-filter: blur(22px) saturate(1.5);
backdrop-filter: blur(22px) saturate(1.5);
}
.gallery-item:hover .text {
opacity: 1;
animation: move-up .33s linear;
padding: 1em;
width: 77.5%;
outline: 2px solid #b60235;
border-radius: 5px;
}
#keyframes move-up {
0% {top:77.5%}
50% {top:63.75%}
100% {top:50%}
}
.w-1 {
grid-column: span 1;
}
.w-2 {
grid-column: span 2;
}
.w-3 {
grid-column: span 3;
}
.w-4 {
grid-column: span 4;
}
.w-5 {
grid-column: span 5;
}
.w-6 {
grid-column: span 6;
}
.h-1 {
grid-row: span 1;
}
.h-2 {
grid-row: span 2;
}
.h-3 {
grid-row: span 3;
}
.h-4 {
grid-row: span 4;
}
.h-5 {
grid-row: span 5;
}
.h-6 {
grid-row: span 6;
}
#media screen and (max-width: 850px) {
.container {
grid-template-columns: repeat(3,1fr);
}
.w-1, .w-2, .w-3, .w-4, .w-5, .w-6 {
grid-column: span 3;
}
}
/* -------------------------------------------------------------------------- */
.closebtn {
position: absolute;
top: 15px;
right: 35px;
color: #f1f1f1;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
}
.closebtn:hover, .closebtn:focus {
color: #bbb;
text-decoration: none;
cursor: pointer;
}
.modal {
display: none;
position: fixed;
z-index: 10;
padding-top: 5%;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.9);
}
.modal-content {
/* leave it centered and just display info underneath -- maybe?
margin-left: 5%;
display: block; */
width: 80%;
max-width: 750px;
max-height: 650px;
object-fit: contain;
}
#caption {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
text-align: center;
color: #ccc;
padding: 10px 0;
height: 150px;
}
.modal-content {
animation-name: zoom;
animation-duration: 0.5s;
}
#keyframes zoom {
from {
transform: scale(0)
} to {
transform: scale(1)
}
}
So, hopefully you'll see that nearly every thing else I'm programming functions as it should, but I can't quite get the data pulled from the API and displaying inside of the modal when opened. I believe my problem rests fully in the JS code and how I'm attempting to fetch and display the data from the API. Also, I have a div with an id of 'info' placed inside of the 'modal' div in my HTML, but I've got a sneaking suspicion that I'm not using that properly either. Let me know if there's any additional information required on my end.
Related
So previously I asked your help to execute a behaviour for when clicking a container div, said div would change height, clicking it again would revert it back to normal, as would clicking a sibling container div.
I also added code for when clicking said div, the img inside would change data-src.
This img div (not the container) has a sibling that is just text. It would change state through css, when the container div is clicked, a bit like the container div itself.
When adding the code for the data-src change, I lost this ability and cant find out why.
Can you help me?
This is my code:
const allImages = document.querySelectorAll('.containertira .imgclasstosize');
const allContainers = document.querySelectorAll('.containertira');
allContainers.forEach(el => {
el.addEventListener('click', function(event) {
const thisImg = el.querySelector('.imgclasstosize');
const thisTxt = el.querySelector('.centered');
const sibling = thisImg.nextElementSibling; // Get the next sibiling
const bigSrc = thisImg.dataset.srcBig;
const allOtherImages = Array.from(allImages).filter(img => {
return img !== thisImg;
});
const isBig = thisImg.classList.contains('big');
1
if (isBig) {
thisImg.classList.remove('big');
thisTxt.classList.remove('left');
// reset to the small image URL
thisImg.src = thisImg.dataset.smallSrc;
} else {
// save the small image URL first:
if (!thisImg.dataset.smallSrc) {
thisImg.dataset.smallSrc = thisImg.src;
}
// change to the big image URL:
thisImg.src = bigSrc;
thisImg.classList.add('big');
thisTxt.classList.add('left');
sibling.classList.remove('hide');
}
allOtherImages.forEach(img => {
img.classList.remove('big');
// reset to the small image URL
if (img.dataset.smallSrc) {
img.src = img.dataset.smallSrc;
}
img.nextElementSibling.classList[isBig ? 'remove' : 'add']("hide");
});
});
}
);
.imgclasstosize{
width: 100%;
object-fit: cover;
position: relative;
}
img.imgclasstosize {
height: 80px;
border: 1px solid gray;
transition : 1.5s all ease;
}
img.imgclasstosize.big {
height: 100%;
transition: 1.5s all ease;
width: 70vw;
margin-left: auto;
}
.containertira {
position: relative;
text-align: center;
color: white;
display: flex;
justify-content: space-between;
}
.imgclasstosize {
transition: all ease 0.5s;
}
.imgclasstosize.big {
/* transform: scale(1.1); */
}
.centered {
opacity: 1;
transition: all ease 0.5s;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: 'blacker_pro_displayregular';
padding-top: 10px;
/* background: linear-gradient(90deg, rgba(33,37,41,1) 0%, rgba(255,255,255,0) 50%); */
color: white;
padding-left: 10px;
padding-bottom: 6.5px;
font-size: 5vh;
display:block;
pointer-events: none;
}
#media screen and (max-width: 960px) {.centered {font-size: 4.6vh;}}
#media screen and (max-width: 500px) {.centered {font-size: 3.5vh;}}
#media screen and (max-width: 375px) {.centered {font-size: 3vh;}}
.centered.left{
top: 0%;
left: 0%;
transform: translate(0%, 0%);
color: black;
padding-left: 10px;
padding-bottom: 6.5px;
transition: all ease 0.5s;
font-size: 7vh;
}
.imgclasstosize.big+.centered.hide {
opacity: 0;
}
<div class="containertira">
<img id="primeiraimagem" class="imgclasstosize" src="//images.impresa.pt/expresso/2021-07-23-1---Minho.png-0c586cc8" data-src-big="//images.impresa.pt/expresso/2021-07-23-960px_minho.png-3938db2f">
<div class="centered">Minho</div>
</div>
<br>
<div class="containertira">
<img id="primeiraimagem" class="imgclasstosize" src="//images.impresa.pt/expresso/2021-07-23-1---Minho.png-0c586cc8" data-src-big="//images.impresa.pt/expresso/2021-07-23-960px_minho.png-3938db2f">
<div class="centered">Minho</div>
</div>
Best regards everyone.
I can't seem to reproduce your error. Tried copying your code and pasting it on a separated HTML file and everything worked.
If you execute this snippet and look at the console, you will see that the classes and properties are changing accordingly to your code.
Maybe your code is executing before some library causing it to not execute something?
const allImages = document.querySelectorAll('.containertira .imgclasstosize');
const allContainers = document.querySelectorAll('.containertira');
allContainers.forEach(el => {
el.addEventListener('click', function(event) {
const thisImg = el.querySelector('.imgclasstosize');
const thisTxt = el.querySelector('.centered');
const sibling = thisImg.nextElementSibling; // Get the next sibiling
const bigSrc = thisImg.dataset.srcBig;
const allOtherImages = Array.from(allImages).filter(img => {
return img !== thisImg;
});
const isBig = thisImg.classList.contains('big');
1
if (isBig) {
thisImg.classList.remove('big');
thisTxt.classList.remove('left');
// reset to the small image URL
thisImg.src = thisImg.dataset.smallSrc;
} else {
// save the small image URL first:
if (!thisImg.dataset.smallSrc) {
thisImg.dataset.smallSrc = thisImg.src;
}
// change to the big image URL:
thisImg.src = bigSrc;
thisImg.classList.add('big');
thisTxt.classList.add('left');
sibling.classList.remove('hide');
}
allOtherImages.forEach(img => {
img.classList.remove('big');
// reset to the small image URL
if (img.dataset.smallSrc) {
img.src = img.dataset.smallSrc;
}
img.nextElementSibling.classList[isBig ? 'remove' : 'add']("hide");
});
});
}
);
.containertira {border:1px black solid;}
<div class="containertira">
<img id="primeiraimagem" class="imgclasstosize" src="img/Tiras/1 - Minho.png" data-src-big="img/INFO joao/joao 2/joao 2/960 px/960px_minho.png">
<div class="centered">Minho</div>
</div>
<div class="containertira">
<img id="primeiraimagem" class="imgclasstosize" src="img/Tiras/1 - Minho.png" data-src-big="img/INFO joao/joao 2/joao 2/960 px/960px_minho.png">
<div class="centered">Minho2</div>
</div>
<div class="containertira">
<img id="primeiraimagem" class="imgclasstosize" src="img/Tiras/1 - Minho.png" data-src-big="img/INFO joao/joao 2/joao 2/960 px/960px_minho.png">
<div class="centered">Minho3</div>
</div>
<div class="containertira">
<img id="primeiraimagem" class="imgclasstosize" src="img/Tiras/1 - Minho.png" data-src-big="img/INFO joao/joao 2/joao 2/960 px/960px_minho.png">
<div class="centered">Minho4</div>
</div>
I'm learning JS by following Wes Bos's class. I'm trying to select buttons and display information every time the user clicks on them. So I add an event listener, however, the fallback function seems to be executed automatically when it is located inside the event listener. I don't understand why 'hello' is displayed automatically while the function youClickTheButton is executed only when the user clicks on a button (see the below code).
Why does this happen?
const myButtons = document.querySelectorAll('.cards button');
const modalOuter = document.querySelector('.modal-outer');
function youClickTheButton(event) {
console.log(event);
modalOuter.classList.add('open');
}
myButtons.forEach(item => item.addEventListener('click', youClickTheButton));
myButtons.forEach(item => item.addEventListener('click', console.log('hello')));
// whenever you click on the button it will open a pop up with a picture with the description
// whenever you click outside of this pop up it should close itself use .closest()
// populate the modal with name and description of the card so you don't have to modify the .html file
// you could also close it by simply pressing escape
const myButtons = document.querySelectorAll('.cards button');
const modalOuter = document.querySelector('.modal-outer');
function youClickTheButton(event) {
console.log(event);
modalOuter.classList.add('open');
}
myButtons.forEach(item => item.addEventListener('click', youClickTheButton));
myButtons.forEach(item => item.addEventListener('click', console.log('hello')));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="../../base.css">
</head>
<body>
<div class="cards">
<div class="card number1" data-description="Wes is cool">
<img src="https://picsum.photos/200?random=1" alt="Wes Bos">
<h2>Wes Bos</h2>
<button>Learn more →</button>
</div>
<div class="card number2" data-description="Scott is neat!">
<img src="https://picsum.photos/200?random=2" alt="Wes Bos">
<h2>Scott Tolinski</h2>
<button>Learn more →</button>
</div>
<div class="card number3" data-description="Kait is beautiful!">
<img src="https://picsum.photos/200?random=3" alt="Wes Bos">
<h2>Kait Bos</h2>
<button>Learn more →</button>
</div>
<div class="card number4" data-description="Snickers is a dog!">
<img src="https://picsum.photos/200?random=4" alt="Wes Bos">
<h2>Snickers the dog</h2>
<button>Learn more →</button>
</div>
</div>
<div class="modal-outer ">
<div class="modal-inner ">
<p>You clicked on the 1st one</p>
</div>
</div>
<style>
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 20px;
padding: 2rem;
}
.card {
background: white;
padding: 1rem;
border-radius: 2px;
}
.card img {
width: 100%;
}
.card h2 {
color: black;
}
.modal-outer {
display: grid;
background: hsla(50, 100%, 50%, 0.7);
position: fixed;
height: 100vh;
width: 100vw;
top: 0;
left: 0;
justify-content: center;
align-items: center;
/* Hide this modal until we need it */
opacity: 0;
pointer-events: none;
transition: opacity 0.2s;
}
.modal-outer img {
width: 100%;
}
.modal-outer.open {
opacity: 1;
pointer-events: all;
}
.modal-inner {
max-width: 600px;
min-width: 400px;
padding: 2rem;
border-radius: 5px;
min-height: 200px;
background: white;
transform: translateY(-200%);
transition: transform 2s;
}
.modal-outer.open .modal-inner {
transform: translateY(0);
}
</style>
<script src="./click-outside_5.js"></script>
</body>
</html>
Check the difference between your two item.addEventListener()s. In the first one, you pass down a function, while in the second one, you call one (no parentheses vs. parentheses).
If you pass down a callback function (i.e. first case), you don't invoke it immediately, you just say that "Here is this function I created, run it every time someone clicks on any of these buttons."
If you want to log from your second function, you need to pass a function, not the result of the function:
myButtons.forEach(item => item.addEventListener('click', () => console.log('hello')))
// OR
function logHello() {
return console.log('hello')
}
myButtons.forEach(item => item.addEventListener('click', logHello));
You can read more about it on MDN.
Anyway, you should not loop over the buttons twice, but I think you're just testing things out at the moment. console.log('hello') could be in your youClickTheButton() function.
I am attempting to make it so that when a user uploads the image file it is automatically used as the background image in the puzzle. I have tried looking up how I would do that and I feel like it would be that hard but I'm just having a brain fart. I am also looking to start a timer once the image gets uploaded but I feel like that is something I ould probably be able to figure out myself but any tips would certainly be appreciated.
html:
<!DOCTYPE html>
<html>
<head>
<script src="functions.js"></script>
<link rel="stylesheet" href="design.css" type="text/css">
<meta charset="UTF-8">
<title>Puzzle</title>
</head>
<body>
<div class="file-upload">
<input class="file-upload__input" type="file" name="picture" id="picture" accept="image/*">
<button class="file-upload__button" type="button">Choose a Photo</button>
<span class="file-upload__label"></span>
</div>
<!--button formating-->
<script>
Array.prototype.forEach.call(document.querySelectorAll(".file-upload__button"), function(button) {
const hiddenInput = button.parentElement.querySelector(".file-upload__input");
const label = button.parentElement.querySelector(".file-upload__label");
const defaultLabelText = "No file(s) selected";
// Set default text for label
label.textContent = defaultLabelText;
label.title = defaultLabelText;
button.addEventListener('click', function(){
hiddenInput.click();
});
hiddenInput.addEventListener('change', function(){
const filenameList = Array.prototype.map.call(hiddenInput.files, function (file){
return file.name;
});
label.textContent = filenameList.join(', ') || defaultLabelText;
label.title = label.textContent;
});
});
</script>
<!--Puzzle-->
<center><div id="table" style="display: table;">
<div id="row1" style="display: table-row;">
<div id="cell11" class="tile1" onClick="clickTile(1,1);"></div>
<div id="cell12" class="tile2" onClick="clickTile(1,2);"></div>
<div id="cell13" class="tile3" onClick="clickTile(1,3);"></div>
</div>
<div id="row2" style="display: table-row;">
<div id="cell21" class="tile4" onClick="clickTile(2,1);"></div>
<div id="cell22" class="tile5" onClick="clickTile(2,2);"></div>
<div id="cell23" class="tile6" onClick="clickTile(2,3);"></div>
</div>
<div id="row3" style="display: table-row;">
<div id="cell31" class="tile7" onClick="clickTile(3,1);"></div>
<div id="cell32" class="tile8" onClick="clickTile(3,2);"></div>
<div id="cell33" class="tile9" onClick="clickTile(3,3);"></div>
</div>
</div>
<button onClick="shuffle();">New Game</button>
</center>
</body>
</html>
CSS:
body {
background: #002a3f;
}
.file-upload {
display: inline-flex;
align-items: center;
font-size: 20px;
}
.file-upload__input {
display: none;
}
.file-upload__button {
-webkit-appearance: none;
background: #009879;
border: 2px solid #00745d;
border-radius: 4px;
outline: none;
padding: 0.5em 0.8em;
margin-right: 15px;
color: white;
font-size: 1em;
font-family: sans-serif;
font-weight: bold;
cursor: pointer;
}
.file-upload__button:active{
background: #00745d;
}
.file-upload__label{
max-width: 250px;
font-size: 0.95em;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.tile1, .tile2, .tile3, .tile4, .tile5, .tile6, .tile7, .tile8, .tile9 {
display: table-cell;
width: 120px;
height: 120px;
border: 1px solid rgb(100, 100, 100);
background: url();
cursor: pointer;
}
.tile1 {background-position: left top;}
.tile2 {background-position: center top;}
.tile3 {background-position: right top;}
.tile4 {background-position: left center;}
.tile5 {background-position: center center;}
.tile6 {background-position: right center;}
.tile7 {background-position: left bottom;}
.tile8 {background-position: center bottom;}
.tile9 {background: rgb(58, 58, 58); cursor: default;}
JS:
function swapTiles(cell1,cell2) {
var temp = document.getElementById(cell1).className;
document.getElementById(cell1).className = document.getElementById(cell2).className;
document.getElementById(cell2).className = temp;
}
function shuffle() {
//Use nested loops to access each cell of the 3x3 grid
for (var row=1;row<=3;row++) { //For each row of the 3x3 grid
for (var column=1;column<=3;column++) { //For each column in this row
var row2=Math.floor(Math.random()*3 + 1); //Pick a random row from 1 to 3
var column2=Math.floor(Math.random()*3 + 1); //Pick a random column from 1 to 3
swapTiles("cell"+row+column,"cell"+row2+column2); //Swap the look & feel of both cells
}
}
}
function clickTile(row,column) {
var cell = document.getElementById("cell"+row+column);
var tile = cell.className;
if (tile!="tile9") {
//Checking if white tile on the right
if (column<3) {
if ( document.getElementById("cell"+row+(column+1)).className=="tile9") {
swapTiles("cell"+row+column,"cell"+row+(column+1));
return;
}
}
//Checking if white tile on the left
if (column>1) {
if ( document.getElementById("cell"+row+(column-1)).className=="tile9") {
swapTiles("cell"+row+column,"cell"+row+(column-1));
return;
}
}
//Checking if white tile is above
if (row>1) {
if ( document.getElementById("cell"+(row-1)+column).className=="tile9") {
swapTiles("cell"+row+column,"cell"+(row-1)+column);
return;
}
}
//Checking if white tile is below
if (row<3) {
if ( document.getElementById("cell"+(row+1)+column).className=="tile9") {
swapTiles("cell"+row+column,"cell"+(row+1)+column);
return;
}
}
}
}
You can use client's image without ever uploading it. The way to do that is to use FileReader API. Check out Mozilla's tutorial of FileReader.readAsDataURL().
When my site is loading up the image always appears straight away (even when it's still loading) however I want it to appear at the exact same times as the text does to make it look like it loading up at the same time. This is the code I have used to make what I see
Javascript
$(window).on("load",function(){
$(".loader-wrapper").fadeOut("slow");
});
$.fn.srcLazy = function (src, callback) {
let elem = $(this);
let img = new Image();
img.addEventListener('load', function () {
elem.on('load', function () {
if (callback) callback();
});
elem.attr('src', img.src);
});
img.src = src;
}
$('#my-image-in-the-page').srcLazy("./images/ohridimage1.jpg", function () {
// Show text here!
});
function showPage() {
document.getElementById("loader").style.display = "none";
document.getElementById("myDiv").style.display = "flex";
}
CSS (LOADING PAGE)
body {
margin: 0;
padding: 0;
width:100vw;
height: 100vh;
background-color: #eee;
}
.content {
display: flex;
justify-content: center;
align-items: center;
width:100%;
height:100%;
}
.loader-wrapper {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: #242f3f;
display:flex;
justify-content: center;
align-items: center;
}
.loader {
display: inline-block;
width: 30px;
height: 30px;
position: relative;
border: 4px solid #Fff;
animation: loader 2s infinite ease;
}
.loader-inner {
vertical-align: top;
display: inline-block;
width: 100%;
background-color: #fff;
animation: loader-inner 2s infinite ease-in;
}
#keyframes loader {
0% { transform: rotate(0deg);}
25% { transform: rotate(180deg);}
50% { transform: rotate(180deg);}
75% { transform: rotate(360deg);}
100% { transform: rotate(360deg);}
}
#keyframes loader-inner {
0% { height: 0%;}
25% { height: 0%;}
50% { height: 100%;}
75% { height: 100%;}
100% { height: 0%;}
CSS
#myDiv {
margin: 0 50px 0 50px;
display: none;
text-align: center;
justify-content: space-between;
align-items: center;
}
.image1 {
position: flex;
right: 0px;
top: 0px;
z-index: -1;
filter: grayscale(100%);
style: float
border: 3px solid #73AD21;
width: 300px;
HTML
<!DOCTYPE HTML>
<HTML lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" type="text/CSS" href="dropdownmenu.css">
<link rel="stylesheet" type="text/CSS" href="rainbowheading.css">
<link rel="stylesheet" type="text/CSS" href="loadingcss.css">
<link rel="stylesheet" type="text/CSS" href="image.css">
<script src="loading.js"></script>
<title> North Macedonia </title>
<script src="js/jquery.js"></script>
<div class="container">
<h1 class="rainbow"> The pearl of the Balkans: Macedonia </h1>
<div class="navbar">
Home
Macedonian Dispora
Cities
<div class="dropdown">
<button class="dropbtn">History
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
Ancient History
Ottoman Period
Yugoslav Period
Modern History
</div>
</div>
</div>
</head>
<script src="loading.js"></script>
<link rel="stylesheet" type="text/css" href="loadingcss.css" />
</head>
<body>
<div class="loader-wrapper">
<span class="loader"><span class="loader-inner"></span></span>
</div>
<div style="display:none;" id="myDiv" class="animate-bottom"></div>
<div>
<h2>Welcome to my website about my country Macedonia (Makedonija)</h2>
<p>Macedonia officially the Republic of North Macedonia, is a country in the Balkan Peninsula in Southeast Europe. It gained its independence in 1991 as one of the successor states of Yugoslavia. A landlocked country, North Macedonia has borders with Kosovo to the northwest, Serbia to the northeast, Bulgaria to the east, Greece to the south, and Albania to the west. It constitutes approximately the northern third of the larger geographical region of Macedonia. The capital and largest city, Skopje, is home to roughly a quarter of the country's 2.06 million inhabitants. <br>
The majority of the residents are ethnic Macedonians, a South Slavic people. Albanians form a significant minority at around 25%, followed by Turks, Romani, Serbs, Bosniaks, and Aromanians.</p>
</div>
<img class="image1" src="./images/ohridimage1.jpg" alt="Smiley face">
</div>
In order to load an image in the background and show it when finished with another elements like text you need to load the image in the Image Object like this:
var img = new Image();
img.addEventListener('load', function () {
// Here you can set the url that was loaded in the background here
// to an actual `<img>` element. Browser uses cache now to load the image quickly.
});
img.src = 'image url here';
As soon as you set the src the image will get loaded but nobody can see it. This will force the browser to load image in the background and cache it. So now when you set that exact src to the img element the browser will use cache to show the image so the image will be loaded quickly with whatever you want to show.
UPDATE --------
You can do it in a better way in jquery:
Let's say you have bunch of images. Then instead of setting their src set their data-src attribute and then use this functionality to do something when all images loaded:
This is the HTML:
<img class="my-images" data-src="<image url 1>" />
<img class="my-images" data-src="<image url 2>" />
<img class="my-images" data-src="<image url 3>" />
<img class="my-images" data-src="<image url 4>" />
This is the JS:
$.fn.whenImagesLoaded = function (callback) {
let found = $(this);
let counter = found.length;
found.each(function (i, item) {
let elem = $(item);
let img = new Image();
img.addEventListener('load', function () {
elem.on('load', function () {
counter--;
if (counter === 0 && callback) callback();
});
elem.attr('src', img.src);
});
img.src = elem.attr('data-src');
});
}
So now you should not set src on the image at first but you have to do this:
$('.my-images').whenImagesLoaded(function () {
// Hide loading text here and show images!
});
JSFiddle
I am trying to build a slider based upon http://css-tricks.com/the-javascript-behind-touch-friendly-sliders/. My goal is to make a horizontal, mobile-only slider that allows you to slide back and forth between the steps in a registration process.
The code works for the most part, but the slider only moves every other touch, and I'm not sure why.
http://codepen.io/anon/pen/zKhao
HTML:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<div class="visible-xs mobile-tabs">
<div class="slider-wrap">
<div class="slider" id="slider">
<div class="holder">
<div class="slide-wrapper">
<h4 class="complete">Before you begin</h4>
</div>
<div class="slide-wrapper">
<h4 class="complete">1. Terms & Conditions</h4>
</div>
<div class="slide-wrapper">
<h4 class="current">2. Teams</h4>
</div>
<div class="slide-wrapper">
<h4>3. Add-Ons</h4>
</div>
<div class="slide-wrapper">
<h4>4. Review & Submit</h4>
</div>
</div>
</div>
</div>
</div>
CSS:
a
{
color: #5fa4db;
text-decoration: none;
}
.mobile-tabs
{
height: 45px;
overflow: hidden;
border-bottom: 1px solid #f1f2f4;
white-space: nowrap;
margin-bottom: 10px;
}
.mobile-tabs h4
{
color: #9fa9b2;
display: inline-block;
padding-right: 10px;
padding-bottom: 10px;
font-weight: bold;
font-size: 18px;
}
.mobile-tabs h4.current
{
border-bottom: 5px solid #5fa4db;
color: #0f2034;
}
.mobile-tabs h4.complete
{
color: #5fa4db;
}
/* CSS for mobile tab slider.
Source: http://css-tricks.com/the-javascript-behind-touch-friendly-sliders/
*/
.mobile-tabs .animate {
transition: transform 0.3s ease-out;
}
.mobile-tabs .slider-wrap {
width: 100%;
position: absolute;
}
.mobile-tabs .slider {
width: 100%;
height: 100%;
overflow: hidden;
}
.mobile-tabs .ms-touch.slider {
overflow-x: scroll;
overflow-y: hidden;
-ms-overflow-style: none;
/* Hides the scrollbar. */
-ms-scroll-chaining: none;
/* Prevents Metro from swiping to the next tab or app. */
-ms-scroll-snap-type: mandatory;
/* Forces a snap scroll behavior on your images. */
-ms-scroll-snap-points-x: snapInterval(0%, 1%);
/* Defines the y and x intervals to snap to when scrolling. */
}
.mobile-tabs .holder {
width: 300%;
overflow-y: hidden;
}
.mobile-tabs .slide-wrapper {
float: left;
position: relative;
overflow: hidden;
}
.mobile-tabs .slide div {
width: 300px;
height: 500px;
z-index: 0;
}
JavaScript:
if (navigator.msMaxTouchPoints) {
$('#slider').addClass('ms-touch');
$('#slider').on('scroll', function () {
$('.slide-image').css('transform', 'translate3d(-' + (100 - $(this).scrollLeft() / 6) + 'px,0,0)');
});
} else {
var slider = {
el: {
slider: $("#slider"),
holder: $(".holder")
},
slideWidth: $('#slider').width(),
touchstartx: undefined,
touchmovex: undefined,
movex: 0,
index: 0,
longTouch: undefined,
init: function () {
this.bindUIEvents();
},
bindUIEvents: function () {
this.el.holder.on("touchstart", function (event) {
slider.start(event);
});
this.el.holder.on("touchmove", function (event) {
slider.move(event);
});
this.el.holder.on("touchend", function (event) {
slider.end(event);
});
},
start: function (event) {
// Test for flick.
this.longTouch = false;
setTimeout(function () {
window.slider.longTouch = true;
}, 250);
// Get the original touch position.
this.oldx = this.movex;
// The movement gets all janky if there's a transition on the elements.
$('.animate').removeClass('animate');
},
move: function (event) {
// Continuously return touch position.
this.touchmovex = event.originalEvent.touches[0].pageX;
// Calculate distance to translate holder.
this.movex = -this.oldx - this.touchmovex;
// Defines the speed the images should move at.
var panx = 100 - this.movex / 6;
if (this.movex < 600) { // Makes the holder stop moving when there is no more content.
this.el.holder.css('transform', 'translate3d(-' + this.movex + 'px,0,0)');
}
},
end: function (event) {
}
};
slider.init();
}
In order to emulate the issue, you'll have to view the code on a mobile device (or use Chrome's mobile emulation) and try to slide the slider back and forth. It will move, but only every other time you attempt to slide it.
I am completely lost, and any help will be appreciated.
This isn't really an answer, per se, but I've decided to throw the entire thing out and use jquery UI's Draggable feature to do what I need to do.
http://jqueryui.com/draggable/#constrain-movement