Add rendered images to array onclick for submission using JS - javascript

I currently have a functionality where the user can upload and display multiple images at once as a preview on top of a modal. Ideally, I would like to allow the user to click on one of the displayed images and add it to an array on a click event so that they can then hit a button to submit the images for processing somewhere. I've tried for a few hours trying to code this out but have hit a brick wall and wouldn't mind some guidance on the matter! I'd love to be able to implement only HTML, CSS and vanilla JS... any suggestions or offerings of help would be appreciated! I searched online a fair bit but couldn't really grasp many of the concepts offered...
<!--Modal code: -->
<div id="simpleModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="closeBtn">×</span>
<h2>Image search and processing: </h2>
</div>
<div class="modal-body">
<form id="modal-form" class="form">
<label for="files">Select multiple files: </label>
<input id="files" target="_blank" type="file" onchange="previewFiles()" multiple/>
<output id="result">
<button type="submit" class="floating-btn" value ="submit">+</button>
<button type="reset" class="floating-btn2" value ="reset" onclick="return hideImage()">x</button>
</form>
</div>
<div id="preview"></div>
</div>
</div>
<script>
//code to render image files to modal
function previewFiles() {
var preview = document.querySelector('#preview');
var files = document.querySelector('input[type=file]').files;
function readAndPreview(file) {
// Make sure `file.name` matches our extensions criteria
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) {
var reader = new FileReader();
reader.addEventListener("load", function () {
var image = new Image();
//styling in JS //
image.height = 160;
image.width = 160;
image.style.flexDirection = "row";
image.title = file.name;
image.src = this.result;
preview.appendChild( image );
}, false);
reader.readAsDataURL(file);
}
}
if (files) {
[].forEach.call(files, readAndPreview);
}
}
//delete form
function hideImage() {
document.getElementById("modal-form").reset(); //reset form
var preview = document.querySelector("#preview");
preview.innerHTML = '' //set preview to null
</script>
}
/* floating buttons: */
.floating-btn{
width: 80px;
height: 80px;
background: #0B406D;
display: flex;
border-radius: 50%;
color: white;
font-size: 40px;
align-items: center;
justify-content: center;
text-decoration: none;
box-shadow: 2px 2px 5px rgba(0,0,0,0.25);
position: fixed;
right: 120px;
bottom: 20px;
outline: blue;
border: none;
cursor: pointer;
}
.floating-btn:hover {
background: #4D89C8;
}
.floating-btn2{
width: 80px;
height: 80px;
background: #0B406D;
display: flex;
border-radius: 50%;
color: white;
font-size: 40px;
align-items: center;
justify-content: center;
text-decoration: none;
box-shadow: 2px 2px 5px rgba(0,0,0,0.25);
position: fixed;
right: 20px;
bottom: 20px;
outline: blue;
border: none;
cursor: pointer;
}
.floating-btn2:hover {
background: #4D89C8;
}
/*Modal styling: */
.modal{
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
position: fixed;
top: 0;
z-index: 1;
display: none;
justify-content: center;
align-items: center;
}
.modal-content{
width: 80%;
height: 80%;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 4px;
padding: 15px;
margin: 20% auto;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px rgba(0, 0, 0, 0.17);
animation-name: modalopen;
animation-duration: 1s;
flex-direction: column;
justify-content: space-around;
}
.modal-header{
font-size:12pt;
color: black;
}
.modal-header h2{
margin: 0;
}
.modal-body{
width: 33.33%;
padding: 5px;
}
.closeBtn{
color: #ccc;
float: right;
font-size: 50px;
}
.closeBtn:hover,.closeBtn:focus{
color: red;
text-decoration: none;
cursor: pointer;
}
#keyframes modalopen{
from{opacity: 0}
to {opacity: 1}
}
/*Image displaying style: */
form{
margin-top:10px;
padding: 5px;
border-radius: 4px;
margin: 0 auto;
}
a img{
float: left;
width: 150px;
height: 150px;
padding-right: 15px;
box-sizing: border-box;
}
img:hover {
transform: scale(1.5);
cursor: pointer;
}

While there can be complicated ways like using FormData and ajax to submit the form it's better to always go with simpler ones. One of such is below
Add a hidden field in your form
<input type="hidden" name="list" id="list">
</form>
Create a variable that will hold an array var sList = []
Now just take this function it appends to that array and serializes that data into the hidden field
function addToArr(item){
var index = sList.indexOf(item);
if (index == -1) { //if not already added
sList.push(item) // add
}
document.getElementById('list').value = JSON.stringify(sList);
}
That's it now in your reader load event listener add this one line
reader.addEventListener("load", function () {
var image = new Image();
//other code
//..
image.src = this.result;
image.onclick = function(){ addToArr(file.name); } // <-- this line
preview.appendChild( image );
}, false);
Now whenever user clicks any image, its name will get into the list array and therefore in hidden field (You may verify using inspect element in devtools). So when you submit the form, on you server side just decode/parse the json and you know which images user wants (the names) by that you can filter on the actual file objects. You haven't told what language you are using at the server so for now this should suffice

Related

Counter made by Javascript and did scrolling in it by using setInterval

Trying to make counter(till 9) by javascript.
Hid the counting of 1 to 9. So, when the user enters the target then scroll the counter by its height so the hidden numbers will be shown. Used two set intervals in the code maybe that's why code not running according to expectation.
codepen link --> https://codepen.io/aryansharma-2002/pen/GRyZJJv
same code pasted here:-
HTML CODE
<div class="input">
<h1 class="heading">Enter a value between 0 and 9</h1>
<div class="left">
<input type="number" id="count" placeholder="Enter Number">
</div>
<div class="right">
<button class="submit-btn" id="submit-btn">Start Counter</button>
</div>
</div>
<div class="output">
<div class="count-output" id="count-output">
<span class="zero" data-count="0">0</span>
<span class="one" data-count="1">1</span>
<span class="two" data-count="2">2</span>
<span class="three" data-count="3">3</span>
<span class="four" data-count="4">4</span>
<span class="five" data-count="5">5</span>
<span class="six" data-count="6">6</span>
<span class="seven" data-count="7">7</span>
<span class="eight" data-count="8">8</span>
<span class="nine" data-count="9">9</span>
</div>
</div>
NO NEED TO READ FULL CSS CODE. THE MAIN CSS is that to div.count-output is given some fixed height and width and then the span tag inside it, is giving 100% height and width so that only one number shown at a time. And all the other numbers is hidden by using overflow: hidden; css rule. So, we will scroll the .count-output by using javascript.
CSS CODE
#import url('https://fonts.googleapis.com/css2?family=Roboto:wght#300;400;500;700;900&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
font-family: 'Roboto', sans-serif;
}
.input{
width: 90%;
margin: 10px auto;
padding: 50px;
background: rgb(2,0,36);
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(44,177,204,0.5374883229659051) 0%, rgba(255,0,218,0.32180204845610116) 100%);
}
.heading{
color: white;
word-spacing: 4px;
font-weight: 700;
margin-bottom: 25px;
text-align: center;
}
.left{
float: left;
width: 50%;
/* border: 2px solid black; */
text-align: center;
}
.right{
width: 50%;
float: right;
/* border: 2px solid black; */
text-align: center;
}
.input::after{
content: "";
display: block;
clear: both;
}
.input input[type="number"]{
width: 50%;
font-size: 20px;
padding: 3px 5px;
outline: none;
border: none;
}
.submit-btn{
background: white;
font-size: 18px;
font-weight: 700;
padding: 10px;
border: none;
border-radius: 15px;
box-shadow: 0px 0px 10px 0px white;
}
.output{
width: 80%;
margin: 20px auto;
background: rgba(0, 0, 0, 0.685);
height: 150px;
display: flex;
justify-content: center;
align-items: center;
}
.output .count-output{
/* border: 2px solid white; */
width: 75px;
/* height: 75px; */
height: 75px;
background: white;
box-shadow: 0px 0px 5px 0px white;
overflow: hidden;
}
.count-output span{
display: inline-block;
width: 100%;
height: 100%;
border-top: 2px solid red;
font-size: 70px;
/* text-align: center; */
display: flex;
justify-content: center;
align-items: center;
/* font-weight: 700; */
}
JS CODE
// when we click the submit button then take the data of the count, then start the counter and from 1 to 9 data hidden there so by js scroll it after 1sec when the target reaches stop and show alert
var input=document.getElementById("count");
var btn=document.getElementById("submit-btn");
var counter=document.getElementById("count-output");
console.log(input);
console.log(btn);
console.log(counter);
var scrollTillTarget=function (target) {
let count=0;
var heightCounter=counter.clientHeight;
console.log(heightCounter);
let intervalId=setInterval(function () {
if (count==target) {
alert("Target has reached");
clearInterval(intervalId);
counter.scrollTo(0,0);
return;
}
// By this line scrolling occurs instantly so the scrolling is not shown. So, used the setInterval so that do small scrolling in small time till the height of counter.
// counter.scrollBy(0,heightCounter);
let currentScroll=0;
let scrollId=setInterval(function () {
if (currentScroll==heightCounter) {
clearInterval(scrollId);
return;
}
counter.scrollBy(0,1);
currentScroll++;
},1);
count++;
},1000);
}
btn.addEventListener("click",function (event) {
var targetCount=input.value;
console.log(targetCount);
// now start the counter and scroll the count-output div to the span where the data-count attribute of span is equal to the targetCount
scrollTillTarget(targetCount);
});
Problem - Want that scrolling must show and also in 1 second. Then next scrolling till the target count.
This problem occuring maybe because the call stack is blocked by the setInterval callback function.
Solved this question by some other technique but want the answer why the above code is having some problem.
Solved Link- https://codepen.io/aryansharma-2002/pen/MWrjELL

Why last function is applying to other elements also on 2nd change

I have created a color changer for different properties like border & background for elements.
Color changing 1 at a time but issue occurs when I click on next element , now the color of both elements changes (on next click) . Is there a way to change only 1 at a time .
The method to solve this can be creating a color-picker with each click and remove when work is done but if possible can the solution be regarding the input color-picker in html
To see the issue :
1)Click on any circle & change its color (works fine)
2)Now click on any other circle and try to change its color
Issue seen : Now on 2nd step both circles color changes at same time
var restyleBG = document.getElementsByClassName("restyleBackground")
for (let i = 0; i < restyleBG.length; i++) {
restyleBG[i].addEventListener('click', changeBGcolor)
function changeBGcolor() {
event.stopPropagation()
var colorPicker = document.getElementById("colorClock");
colorPicker.click();
colorPicker.addEventListener('input', function() {
var colorPickerVal = colorPicker.value;
restyleBG[i].style.backgroundColor = colorPickerVal;
})
}
}
#clockStyleCircle {
position: absolute;
width: 16vw;
height: 16vw;
text-align: center;
padding: 0%;
top: 28.5%;
left: 28.5%;
border-radius: 50%;
border: 3px solid black;
background-color: rgb(255, 233, 35);
z-index: 1;
}
#clockOuterCircle {
display: flex;
justify-content: center;
align-items: center;
width: 42vw;
height: 42vw;
margin: auto;
border-radius: 50%;
border: 4px solid rgb(255, 62, 62);
background-color: rgb(253, 133, 133);
user-select: none;
}
#another-circle {
width: 50px;
height: 50px;
border-radius: 50%;
border: 4px green solid;
background-color: blue;
position: absolute;
top: 20px;
left: 20px;
}
.colour-picker {
display: none;
}
<body>
<input type="color" name="colorClock" id="colorClock">
<div id="clockOuterCircle" class="restyleBackground">
<div id="clockStyleCircle" class="restyleBackground"></div>
</div>
<!-- another circle -->
<div id="another-circle" class="restyleBackground"></div>
<!-- ... -->
</body>
Thanks for help in advance
What is going on ?
Your problem here is that for every time you click on a circle you add an EventListener to your colorPicker which then changes the color of the circle. So every time you change the value of the colorPicker, the circles that got clicked on change their color.
How to achieve what you want
You could try to remove the EventListener after the change happened, but this isn't good practice. What you should do is create a global variable that holds the index to the current circle of the circles array that got clicked on and add an EventListener to the input element, that changes the color of the circle with the index of that reference variable. Below you can see the code.
var restyleBG = document.getElementsByClassName("restyleBackground")
let current = 0;
var colorPicker = document.getElementById("colorClock");
colorPicker.addEventListener('input', function() {
restyleBG[current].style.backgroundColor = colorPicker.value;
})
for (let i = 0; i < restyleBG.length; i++) {
restyleBG[i].addEventListener('click', changeBGcolor)
function changeBGcolor() {
event.stopPropagation()
colorPicker.click();
current = i;
}
}
#clockStyleCircle {
position: absolute;
width: 16vw;
height: 16vw;
text-align: center;
padding: 0%;
top: 28.5%;
left: 28.5%;
border-radius: 50%;
border: 3px solid black;
background-color: rgb(255, 233, 35);
z-index: 1;
}
#clockOuterCircle {
display: flex;
justify-content: center;
align-items: center;
width: 42vw;
height: 42vw;
margin: auto;
border-radius: 50%;
border: 4px solid rgb(255, 62, 62);
background-color: rgb(253, 133, 133);
user-select: none;
}
#another-circle {
width: 50px;
height: 50px;
border-radius: 50%;
border: 4px green solid;
background-color: blue;
position: absolute;
top: 20px;
left: 20px;
}
.colour-picker {
display: none;
}
<body>
<input type="color" name="colorClock" id="colorClock">
<div id="clockOuterCircle" class="restyleBackground">
<div id="clockStyleCircle" class="restyleBackground"></div>
</div>
<!-- another circle -->
<div id="another-circle" class="restyleBackground"></div>
<!-- ... -->
</body>
As I said in comments on every click you were adding new event listeners so after the second click there you have more than one event registered. so from the second click, your input Listener will call all previous color changes, that's why it is changing the color of all 3 circles.
I have removed the listener after every color change so this code works fine
Check the console once to get the listener calls, also check by removing the "removeEventListener" and check how many "input event called" will appear after changing color if all three circles (this will help to understand the problem in your code )
Also, you may not be needed to add an input evenlistener inside the loop, I have approached same to understand the problem in your code
You can add eventListner outside the loop
So that you may not need to remove it
var restyleBG = document.getElementsByClassName("restyleBackground")
var colorPicker = document.getElementById("colorClock");
var element;
for (let i = 0; i < restyleBG.length; i++) {
restyleBG[i].addEventListener('click', ()=> changeBGcolor(restyleBG[i]))
}
function changeBGcolor(restyle) {
event.stopPropagation()
colorPicker.click();
element = restyle
colorPicker.addEventListener('input', respond)
}
function respond() {
var colorPickerVal = colorPicker.value;
console.log('input event called')
element.style.backgroundColor = colorPickerVal;
}
colorPicker.addEventListener('change',function (){
console.log('input event removed')
colorPicker.removeEventListener('input', respond)
})
#clockStyleCircle {
position: absolute;
width: 16vw;
height: 16vw;
text-align: center;
padding: 0%;
top: 28.5%;
left: 28.5%;
border-radius: 50%;
border: 3px solid black;
background-color: rgb(255, 233, 35);
z-index: 1;
}
#clockOuterCircle {
display: flex;
justify-content: center;
align-items: center;
width: 42vw;
height: 42vw;
margin: auto;
border-radius: 50%;
border: 4px solid rgb(255, 62, 62);
background-color: rgb(253, 133, 133);
user-select: none;
}
#another-circle {
width: 50px;
height: 50px;
border-radius: 50%;
border: 4px green solid;
background-color: blue;
position: absolute;
top: 20px;
left: 20px;
}
.colour-picker {
display: none;
}
<body>
<input type="color" name="colorClock" id="colorClock">
<div id="clockOuterCircle" class="restyleBackground" >
<div id="clockStyleCircle" class="restyleBackground" ></div>
</div>
<!-- another circle -->
<div id="another-circle" class="restyleBackground" ></div>
<!-- ... -->
</body>

How to detect iOS and show banner once in pure Javascript?

This script works to show a banner on all platforms, but I would like it to show only when it detects iOS devices.
<---- Good Script------->
const cookieBanner = document.querySelector(".cookie-banner");
const cookieButton = document.querySelector(".cookie-btn");
cookieButton.addEventListener("click", () => {
cookieBanner.classList.remove("active");
localStorage.setItem("cookieBannerDisplayed", "true");
});
setTimeout(() => {
if (!localStorage.getItem("cookieBannerDisplayed")) {
cookieBanner.classList.add("active");
}
}, 2000);
I tried doing this below but it didn't work. Instead, it completely no longer works on any platform; not on Safari iOS, Safari, Chrome nor Firefox.
<---- Bad Script------->
const cookieBanner = document.querySelector(".cookie-banner");
const cookieButton = document.querySelector(".cookie-btn");
var t = window.navigator.userAgent,
a = window.navigator.platform;
cookieButton.addEventListener("click", () => {
cookieBanner.classList.remove("active");
localStorage.setItem("cookieBannerDisplayed", "true");
});
setTimeout(() => {
if (!localStorage.getItem("cookieBannerDisplayed")) && if (!["iPhone", "iPad", "iPod"].indexOf(a)) {
cookieBanner.classList.add("active");
}
}, 2000);
Edit: I'm including the CSS code that it 'active'ates.
What this does is show the banner sliding into the users view:
.cookie-banner{
display: flex;
flex-direction: row;
background-color:#282828;
opacity: 0.90;
color: #fff;
width: 100%;
position: fixed;
top: -100%;
right: 0;
z-index: 100;
align-items: center;
justify-content: start;
font-size: 0.875rem;
line-height: 1.5;
font-weight: 400;
box-shadow: rgba(255, 255, 255, 0.05) 0px -1px 0px 0px;
padding: 1rem 1.25rem 1rem 18px;
transition: 400ms;
}
.cookie-banner a{
color: #f5f6fa;
text-decoration: underline;
}
.cookie-btn {
display: inline-flex;
background-color: transparent;
border: 1px solid #fff;
color: #f5f6fa;
padding: .2rem 1rem;
font-size: .875rem;
margin-bottom: 9px;
margin-left: 30px;
border-radius: 50px;
cursor: pointer;
}
.cookie-banner.active {
top: 0;
}
Below is the main page that the user is looking at basically the body is normally filled with article words.
On the main page I have this
<head>
<link rel="stylesheet" type="text/css" href="assets/css/ios-banner.css">
</head>
<body>
div class="cookie-banner">
<p> We use cookies to provide you with the best experience on our site. Learn More </p>
<button class="cookie-btn">Okay</button>
</div>
<script src="assets/js/ios-banner.js"></script>
</body>
Any ideas what I'm doing wrong?
Your issue was you had bad logic in your set timeout callback, and you have not got a proper opening tag to your banner div in the html you list.
Also in general try to use good naming practices instead of variable names like 't' and 'a' as it will make your code hard to read and therefore hard to understand.
Here is what worked for me:
The CSS (unchanged)
.cookie-banner {
display: flex;
flex-direction: row;
background-color: #282828;
opacity: 0.90;
color: #fff;
width: 100%;
position: fixed;
top: -100%;
right: 0;
z-index: 100;
align-items: center;
justify-content: start;
font-size: 0.875rem;
line-height: 1.5;
font-weight: 400;
box-shadow: rgba(255, 255, 255, 0.05) 0px -1px 0px 0px;
padding: 1rem 1.25rem 1rem 18px;
transition: 400ms;
}
.cookie-banner a {
color: #f5f6fa;
text-decoration: underline;
}
.cookie-btn {
display: inline-flex;
background-color: transparent;
border: 1px solid #fff;
color: #f5f6fa;
padding: .2rem 1rem;
font-size: .875rem;
margin-bottom: 9px;
margin-left: 30px;
border-radius: 50px;
cursor: pointer;
}
.cookie-banner.active {
top: 0;
}
The HTML, used ids for selectors and fixed the opening div tag error
<div id="cookie-banner-id" class="cookie-banner">
<p>
We use cookies to provide you with the best experience on our site.
<a href="https://somewebsitehere.website/privacy.php">
Learn More
</a>
</p>
<button id="cookie-btn-id" class="cookie-btn">Okay</button>
</div>
The JS
const cookieBanner = document.getElementById("cookie-banner-id");
const cookieButton = document.getElementById("cookie-btn-id");
const userAgentValue = navigator.userAgent;
let cookieBannerDisplayed = false;
let activeClassAdded = false; // so we only add active once
cookieButton.addEventListener("click", () => {
cookieBanner.classList.remove("active");
localStorage.setItem("cookieBannerDisplayed", true);
});
setTimeout(() => {
if (!localStorage.getItem("cookieBannerDisplayed")){
["iPhone", "iPad", "iPod"].forEach((targetBrowserName) => {
if (userAgentValue.includes(targetBrowserName) && !activeClassAdded) {
cookieBanner.classList.add("active");
activeClassAdded = true;
}
});
}
}, 2000);
When testing you may need to clear local storage for each test run, else switch out the local storage for a local variable.

Properly position two DIVs - One as a fixed sidebar, and other as expanding

I have the following code to create a simple website with two sections; a sidebar and the main content:
html, body {
height: 100%;
margin: 0;
}
.container {
display: flex;
flex-direction: row;
height: 100%;
}
.sidebar {
background-color: #FFF5BA;
width: 200px;
}
.main {
background-color: rgba(248, 248, 248, 1);
flex-grow: 1;
}
The HTML is as goes:
<div class="container">
<div class="sidebar">
<div class="main">
</div>
The problem I have happens when you dynamically add more content to the main section with JavaScript. If you add enough to require scrolling, then the sidebar will not grow as you scroll down.
Ideally I would like to leave the sidebar as fixed content that scrolls with you, but I have read that you cannot combine flexbox with position: fixed.
If you need to see the complete here it is:
https://codepen.io/wulfenn/pen/LYNRNEv (apologies in advance if the code is sloppy; I am still learning)
Thank you for your help!
I have updated html code by moving modal code outside of container class.
I have used position:sticky on sidebar class and assign height:100vh. so that it would not scroll if main content height is bigger than 100vh.
I have also added dummy book-add div to create more height of main content.
View results in full screen and only first add button will work.
// Declare the Object constructor.
function Book(title, author, pages, isbn, read, bookURL) {
this.title = title;
this.author = author;
this.pages = pages;
this.isbn = isbn;
this.read = read;
this.bookURL = bookURL;
}
Book.prototype.toggleStatus = function () {
if (this.read == 'Read') {
this.read = 'Not Read';
return 1;
} else if (this.read == 'Not Read') {
this.read = 'Reading';
return 2;
} else if (this.read == 'Reading') {
this.read = 'Read';
return 3;
}
}
// Initalize library variables.
let myLibrary = [];
const container = document.querySelector('.books-container');
// Display and Hide the "Add a Book" form.
const popup = document.querySelector('.form-popup');
const addBtn = document.querySelector('.add-btn');
const cancelBtn = document.getElementById('cancel-btn');
addBtn.addEventListener('click', function () {
popup.style.display = 'block'; // Show
})
cancelBtn.addEventListener('click', function () {
popup.style.display = 'none'; // Hide
})
// #### Book Form Start
// ##
// Get the form values
const form = document.getElementById('form1');
form.addEventListener('submit', function (event) {
const title = document.forms[0].elements[1].value;
const author = document.forms[0].elements[2].value;
const pages = document.forms[0].elements[3].value;
const isbn = document.forms[0].elements[4].value;
const bookURL = document.forms[0].elements[0].value;
// Check which radio button has been selected.
let read = '';
if (document.getElementById('read').checked) {
read = 'Read';
} else if (document.getElementById('unread').checked) {
read = 'Not Read';
} else {
read = 'Reading';
}
// Prevent page from refreshing and closing the form popup.
event.preventDefault();
popup.style.display = 'none';
// Add our book.
addBookToLibrary(title, author, pages, isbn, read, bookURL);
// Display the books and reset the form.
render();
form.reset();
})
// Display our cover preview.
const cover = document.querySelector('.cover-preview');
const isbnField = document.getElementById('isbn'); // In case ISBN has been typed
const coverURL = document.getElementById('url'); // In case URL has been used.
coverURL.addEventListener('change', function () {
cover.style.background = `url(${document.forms[0].elements[0].value})`;
cover.style.backgroundSize = 'cover';
})
isbnField.addEventListener('change', function () {
if (document.forms[0].elements[0].value == '') { // URL takes preference as it's chosen by user.
cover.style.background = `url(http://covers.openlibrary.org/b/isbn/${document.forms[0].elements[4].value}-M.jpg)`;
cover.style.backgroundSize = 'cover';
}
})
// Add a given book to myLibrary array
function addBookToLibrary(title, author, pages, isbn, read, bookURL) {
let book = new Book(title, author, pages, isbn, read, bookURL);
myLibrary.push(book);
}
// ##
// #### Book Form End
// Display the books in our HTML
function render() {
// Clear our space first.
const existingDivs = document.querySelectorAll('[data-book]');
existingDivs.forEach((div) => {
div.remove();
});
for (let i = 0; i < myLibrary.length; i++) {
let element = document.createElement('div');
element.classList.add('book');
// Determine our cover. URL overrides ISBN.
if (myLibrary[i]['bookURL']) {
element.style.background = `url(${myLibrary[i]['bookURL']})`;
} else {
element.style.background = `url(http://covers.openlibrary.org/b/isbn/${myLibrary[i]['isbn']}-M.jpg)`;
}
element.style.backgroundSize = 'cover';
element.setAttribute("data-book", i);
// Create our mouse enter divs to display book information.
let infoDiv = document.createElement('div');
infoDiv.classList.add('book-info');
infoDiv.style.display = 'none'; // Set to not display by deafult until mouse enter.
let titleDiv = document.createElement('div');
titleDiv.classList.add('info-title');
titleDiv.textContent = myLibrary[i]['title'];
let authorDiv = document.createElement('div');
authorDiv.classList.add('info-author');
authorDiv.textContent = `by ${myLibrary[i]['author']}`;
let pagesDiv = document.createElement('div');
pagesDiv.classList.add('info-pages');
pagesDiv.textContent = `Pages: ${myLibrary[i]['pages']}`;
// Create our status buttons and our delete button.
let buttonsDiv = document.createElement('div');
buttonsDiv.classList.add('info-buttons');
let readTag = document.createElement('button');
readTag.classList.add('button-status');
readTag.setAttribute('data-bookstatus', i);
if (myLibrary[i]['read'] == 'Read') {
readTag.style.background = '#EBFFE5';
readTag.textContent = '✔';
} else if (myLibrary[i]['read'] == 'Not Read') {
readTag.style.background = '#FFC1B1';
readTag.textContent = '❌';
} else {
readTag.style.background = '#FFFFEA';
readTag.textContent = '📖';
}
let removeTag = document.createElement('button');
removeTag.classList.add('button-status');
removeTag.textContent = '🗑';
removeTag.setAttribute("data-bookremove", i);
// Add everything together
buttonsDiv.appendChild(readTag);
buttonsDiv.appendChild(removeTag);
infoDiv.appendChild(titleDiv);
infoDiv.appendChild(authorDiv);
infoDiv.appendChild(pagesDiv);
infoDiv.appendChild(buttonsDiv);
element.appendChild(infoDiv);
// Insert the finished product
container.insertBefore(element, container.firstChild);
}
// Display book information on mouseover
const bookFrames = Array.from(document.querySelectorAll('.book'));
bookFrames.forEach((bookFrame) => {
bookFrame.addEventListener('mouseenter', function (e) {
bookFrame.firstChild.style.display = 'block';
});
});
bookFrames.forEach((bookFrame) => {
bookFrame.addEventListener('mouseleave', function (e) {
bookFrame.firstChild.style.display = 'none';
});
});
// Add functionality to our status and delete buttons
const statusButtons = Array.from(document.querySelectorAll('button[data-bookstatus'));
statusButtons.forEach((button) => {
button.addEventListener('click', function () {
let index = button.getAttribute('data-bookstatus');
let x = myLibrary[index].toggleStatus();
switch (x) {
case 1:
button.style.background = '#FFC1B1';
button.textContent = '❌';
break;
case 2:
button.style.background = '#FFFFEA';
button.textContent = '📖';
break;
case 3:
button.style.background = '#EBFFE5';
button.textContent = '✔';
break;
}
});
});
//Remove button
const removeButtons = Array.from(document.querySelectorAll('button[data-bookremove]'));
removeButtons.forEach((button) => {
button.addEventListener('click', function () {
let index = button.getAttribute('data-bookremove');
const bookToRemove = document.querySelector(`div[data-book='${index}']`);
bookToRemove.remove(); // Remove it from the DOM.
myLibrary.splice(index, 1); // Remove it from our array so it does not render again.
});
});
}
#import url("https://fonts.googleapis.com/css?family=Special Elite&display=swap");
html,
body {
height: 100%;
margin: 0;
font-family: "Special Elite";
}
.container {
width: 100%;
display: flex;
justify-content: space-evenly;
align-items: flex-start;
}
.sidebar {
position: sticky;
top: 0%;
left: 0%;
background-color: #fff5ba;
width: 200px;
height:100vh;
}
.main {
background-color: rgba(248, 248, 248, 1);
flex-grow: 1;
}
.books-container {
background-color: rgba(248, 248, 248, 1);
margin-left: auto;
margin-right: auto;
position: relative;
top: 10%;
left: 50%;
transform: translate(-50%);
display: flex;
flex-wrap: wrap;
}
.book {
width: 200px;
height: 300px;
margin: 50px 25px;
border: 1px solid rgba(0, 0, 0, 0.5);
border-radius: 5px;
box-shadow: 3px 3px 5px 0px black;
}
.book-info {
background-color: rgba(0, 0, 0, 0.6);
width: 200px;
height: 300px;
}
.info-title {
position: relative;
top: 15%;
color: white;
font-size: 20px;
text-align: center;
text-shadow: 3px 3px 1px black;
}
.info-author {
position: relative;
top: 20%;
color: white;
font-size: small;
font-style: italic;
text-align: center;
text-shadow: 1px 1px 1px black;
}
.info-pages {
position: relative;
top: 23%;
color: #fff5ba;
font-size: 11px;
font-weight: bold;
text-align: center;
text-shadow: 1px 1px 1px black;
}
.info-buttons {
position: relative;
top: 40%;
left: 20%;
}
.button-status {
width: 50px;
height: 50px;
outline: none;
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 50px;
margin-left: 5px;
box-shadow: 3px 3px 5px 0px rgba(0, 0, 0, 0.8);
background-color: rgba(255, 255, 255, 0.8);
font-size: 25px;
}
.button-status:hover {
transform: scale(1.1);
}
.book-add {
width: 200px;
height: 300px;
margin: 50px 25px;
border: 1px solid rgba(0, 0, 0, 0.5);
border-radius: 5px;
box-shadow: 3px 3px 5px 0px black;
}
.add-btn {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 100px;
background: inherit;
outline: none;
border-radius: 100px;
font-size: 4em;
}
.add-btn:hover {
background-color: #fff5ba;
}
.cover {
width: 200px;
height: 300px;
}
.cover-preview-container {
position: absolute;
right: 10%;
text-align: center;
}
.cover-preview {
width: 100px;
height: 150px;
border: 1px solid black;
position: relative;
left: 30%;
margin-bottom: 10px;
margin-top: 5px;
}
.form-popup {
display: none;
position: fixed;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
}
.book-form-container {
position: fixed;
z-index: 1;
padding-top: 100px;
left: 50%;
top: 50%;
width: 500px;
height: 500px;
transform: translate(-50%, -50%);
overflow: hidden;
background-color: #836953;
border: 1px solid black;
border-radius: 5px;
}
.form-header {
position: absolute;
top: 0px;
background: url(assets/books.jpeg);
background-size: cover;
width: 500px;
height: 150px;
border-bottom: 1px solid black;
font-size: 4em;
text-align: center;
color: white;
text-shadow: 3px 3px 1px black;
}
.form-footer {
position: absolute;
bottom: 0px;
background: url(assets/wood.jpeg);
background-size: cover;
width: 500px;
height: 150px;
border-bottom: 1px solid black;
font-size: 4em;
text-align: center;
color: white;
text-shadow: 3px 3px 1px black;
}
.book-form {
position: absolute;
top: 150px;
bottom: 150px;
width: 500px;
padding: 20px;
line-height: 20px;
background-color: #fff5ba;
}
.entry-box {
height: 20px;
border-radius: 5px;
outline: none;
background-color: rgba(240, 240, 240, 0.5);
margin-bottom: 15px;
}
.form-btn {
margin: 40px 40px;
width: 100px;
height: 50px;
border: 1px solid white;
border-radius: 10px;
background: inherit;
color: white;
font-family: inherit;
font-size: 20px;
box-shadow: 3px 3px 1px 1px black;
outline: none;
}
.form-btn:hover {
transform: scale(1.1);
}
<div class="container">
<div class="sidebar">
<div class="my-library">My Library</div>
<div class="menu">Home</div>
</div>
<div class="main">
<div class="books-container">
<div class="book-add">
<button class="add-btn">+</button>
</div>
<div class="book-add">
<button class="add-btn">+</button>
</div>
<div class="book-add">
<button class="add-btn">+</button>
</div>
<div class="book-add">
<button class="add-btn">+</button>
</div>
<div class="book-add">
<button class="add-btn">+</button>
</div>
<div class="book-add">
<button class="add-btn">+</button>
</div>
<div class="book-add">
<button class="add-btn">+</button>
</div>
<div class="book-add">
<button class="add-btn">+</button>
</div>
</div>
</div>
</div>
<div class="form-popup">
<div class="book-form-container">
<div class="form-header"><br />Add a Book</div>
<div class="book-form">
<form id="form1">
<div class="cover-preview-container">Cover
<div class="cover-preview"></div>
<div><span style="font-size: small">Or enter the link to the cover image:</span> <br /><input class="entry-box" id="url" type="url" placeholder="https://"></div>
</div>
<div>
<label for="title"> Title </label><br />
<input class="entry-box" type="text" id="title" name="title" placeholder="Enter the book title." required>
</div>
<div>
<label for="author"> Author</label><br />
<input class="entry-box" type="text" id="author" name="author" placeholder="Enter the Author." required>
</div>
<div>
<label for="pages"> Pages</label><br />
<input class="entry-box" type="number" id="pages" name="pages" min="1" placeholder="0" required>
</div>
<div>
<label for="isbn"> ISBN <span style="font-size: small"><i><sup>what is this?</sup></i></span></label><br />
<input class="entry-box" type="text" id="isbn" name="isbn" placeholder="(optional)">
</div>
<div style="text-align: center">
<label for="read">Read</label>
<input id="read" type="radio" name="status" value="read" required>
<label for="unread">Not Read</label>
<input id="unread" type="radio" name="status" value="unread" required>
<label for="reading">Reading</label>
<input id="reading" type="radio" name="status" value="reading" required>
</div>
</form>
</div>
<div class="form-footer">
<button id="form-add-btn" type="submit" class="form-btn" form="form1">Add</button>
<button id="cancel-btn" class="form-btn">Cancel</button>
</div>
</div>
</div>

adding text to an image, no canvas, vanilla javascript

I am working on a meme generator project, it's to practice DOM manipulation. So, I'm supposed to use vanilla JavaScript and no canvas. I'm having trouble finding answer with these parameters. I am almost done with the project, I just need to add text to the picture that they submitted.
I have tried using innerText and innerHTML they seem to just replace it. I've tried append child, similar to how I got the appendchild with the image, but I either get an error or replace the image.
I was pretty sure that I needed to add it add the picture with JavaScript then style it with CSS. Maybe just add a class with classList.
console.log('Currentfile: memegenerator');
let img = document.getElementsByTagName('img');
addEventListener('submit', function(e) {
e.preventDefault();
let UIurl = document.getElementById('picurl');
let memeToBe = UIurl.value;
let img = document.createElement('img');
img.setAttribute('src', memeToBe);
img.setAttribute('class', 'meme');
// append to the document with set attribute using said variable
let memeLocation = document.getElementById('location');
memeLocation.appendChild(img);
//url for pic test
//https://www.fillmurray.com/640/360
//add text to image
//get text values
let inputText = document.getElementById('text_top');
let textValue = inputText.value;
addEventListener('click', function(e) {
let clickedElement = e.target;
console.log(clickedElement);
let targetCheck = clickedElement.classList.contains("meme");
if (targetCheck) {
clickedElement.remove();
}
})
h1 {
color: navy;
}
.center {
text-align: center;
}
.main {
width: 50%;
margin: 0 auto;
background-color: lightblue;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
}
.meme {
width: 99%;
/* margin: 2 auto; */
justify-content: center;
background-color: lightblue;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
}
main:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
meme:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
body {
background-color: #f0feff;
}
.button {
float: right;
}
/* divider styles */
hr.rounded {
border-top: 2px solid #bbb;
border-radius: 5px;
width: 90%;
}
.border_lower {
border-bottom: 1px solid #bbb;
border-radius: 5px;
width: 90%;
}
/* form styles */
form {
width: 60%;
margin: 0 auto;
}
form input {
margin: 2px;
}
form label {
margin: 2px;
}
/* Container holding the image and the text */
.container {
position: relative;
text-align: center;
color: white;
}
/* Bottom left text */
.bottom-left {
position: absolute;
bottom: 8px;
left: 16px;
}
/* Top left text */
.location {
position: absolute;
top: 8px;
left: 16px;
}
<main class="main">
<h1 class="center">MEME GENERATOR!</h1>
<hr class="rounded" />
<form action="#" class="form">
<label for="text_top">Text Upper:</label>
<input type="text" name="text_top" id="text_top" /><br />
<label for="text_lower">Text Lower:</label>
<input type="text" name="text_lower" id="text_lower" /><br />
<label for="picturl">Picture:</label>
<input type="url" name="picurl" id="picurl" /><br /><br /><br />
<input class="button " type="submit" value="Add Meme:" /><br />
<hr />
</form>
<div id="location"></div>
<hr class="border_lower" />
<p class="center"><small>Thanks for visiting!</small></p>
</main>
Since this isn't about saving the images but just for display purposes, I got it working.
The main problem that you were having is your approach seemed to focus more on the javascript side of things but missed out on the CSS part of it.
There are multiple ways to put images behind text, the most common two are:
Setting the images as a background image on the parent element (ie div) then just setting the text within that element
Using CSS to absolute position the text on the image and use z-index to layer them
For my answer I chose #2.
Besides misc code clean ups, the main function that I did was:
I created a div, and gave it a class of meme
I added the image to that div
I added the top and bottom text to their own divs and append those to the meme div
Using CSS, I positioned the top and bottom text above the image
A few other things, when adding an eventListeners unless it is absolutely needed, I recommend tying them to a specific element and not just the document (or nothing at all which I believe is document anyway). By applying it to the document, any click will be processed, but by tying it to the element, only clicks on that element will be processed.
console.log('Currentfile: memegenerator');
let img = document.getElementsByTagName('img');
let form = document.querySelector('form');
form.addEventListener('submit', function(e) {
e.preventDefault();
let meme = document.createElement("div");
let top_text = document.createElement("div");
let bottom_text = document.createElement("div");
let img = document.createElement("img");
let btn = document.createElement("button");
btn.setAttribute("type","button");
img.src = document.getElementById('picurl').value;
top_text.classList.add("top_text");
top_text.innerHTML = document.getElementById('text_top').value;
bottom_text.classList.add("bottom_text");
bottom_text.innerHTML = document.getElementById('text_lower').value;
btn.innerHTML = "REMOVE";
meme.classList.add("meme");
meme.appendChild(top_text);
meme.appendChild(bottom_text);
meme.appendChild(img);
meme.appendChild(btn);
let memeLocation = document.getElementById('location');
memeLocation.appendChild(meme);
document.getElementById('picurl').value = "";
document.getElementById('text_top').value = "";
document.getElementById('text_lower').value = "";
btn.addEventListener('click', function(e) {
meme.remove();
})
});
h1 {
color: navy;
}
.center {
text-align: center;
}
.main {
width: 50%;
margin: 0 auto;
background-color: lightblue;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
}
.meme {
width: 99%;
/* margin: 2 auto; */
justify-content: center;
border-radius: 0.5rem;
border: 0.08rem solid black;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
position:relative;
}
.top_text{
position:absolute;
top:10px;
left:30px;
color:#ffffff;
z-index:3
}
.bottom_text{
position:absolute;
bottom:20px;
left:30px;
color:#ffffff;
z-index:3
}
.meme img{
max-width:100%;
z-index:2
}
main:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
meme:hover {
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
}
body {
background-color: #f0feff;
}
.button {
float: right;
}
/* divider styles */
hr.rounded {
border-top: 2px solid #bbb;
border-radius: 5px;
width: 90%;
}
.border_lower {
border-bottom: 1px solid #bbb;
border-radius: 5px;
width: 90%;
}
/* form styles */
form {
width: 60%;
margin: 0 auto;
}
form input {
margin: 2px;
}
form label {
margin: 2px;
}
/* Container holding the image and the text */
.container {
position: relative;
text-align: center;
color: white;
}
/* Bottom left text */
.bottom-left {
position: absolute;
bottom: 8px;
left: 16px;
}
/* Top left text */
.location {
position: absolute;
top: 8px;
left: 16px;
}
<main class="main">
<h1 class="center">MEME GENERATOR!</h1>
<hr class="rounded" />
<form action="#" class="form">
<label for="text_top">Text Upper:</label>
<input type="text" name="text_top" id="text_top" /><br />
<label for="text_lower">Text Lower:</label>
<input type="text" name="text_lower" id="text_lower" /><br />
<label for="picturl">Picture:</label>
<input type="url" name="picurl" value="https://www.fillmurray.com/640/360" id="picurl" /><br /><br /><br />
<input type="submit" value="Add Meme:" /><br />
<hr />
</form>
<div id="location"></div>
<hr class="border_lower" />
<p class="center"><small>Thanks for visiting!</small></p>
</main>

Categories

Resources