I am trying to make a signature pad but the JS (scribbling line) works in a developing sandbox but not when I combine the files. I have placed my CSS in between the head tags and script just before closing the body tags to allow the JS to run after the other components have run. Not sure what is causing it not to run. Beginner here, so I apologise if my question is too entry-level. Any assistance would be greatly appreciated.
<html>
<head>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
height: 100vh;
width: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 0;
padding: 32px 16px;
font-family: Helvetica, Sans-Serif;
}
.signature-pad {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
font-size: 10px;
width: 100%;
height: 100%;
max-width: 700px;
max-height: 460px;
border: 1px solid #e8e8e8;
background-color: #fff;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.08) inset;
border-radius: 4px;
padding: 16px;
cursor: crosshair;
cursor: url('https://staging-dovetail.kinsta.cloud/wp-content/uploads/2021/05/Pen-Cursor-1.png') 53 53, crosshair;
}
}
.signature-pad::before,
.signature-pad::after {
position: absolute;
z-index: -1;
content: "";
width: 40%;
height: 10px;
bottom: 10px;
background: transparent;
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.4);
}
.signature-pad::before {
left: 20px;
-webkit-transform: skew(-3deg) rotate(-3deg);
transform: skew(-3deg) rotate(-3deg);
}
.signature-pad::after {
right: 20px;
-webkit-transform: skew(3deg) rotate(3deg);
transform: skew(3deg) rotate(3deg);
}
.signature-pad--body {
position: relative;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
border: 1px solid #f4f4f4;
}
canvas {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: 4px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.02) inset;
}
.signature-pad--footer {
color: #C3C3C3;
text-align: center;
font-size: 1.2em;
margin-top: 8px;
}
.signature-pad--actions {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
margin-top: 8px;
}
#github img {
border: 0;
}
#fileupload {
display: none;
}
form {
display: table-row;
margin-right: 5px;
}
span[role=button] {
display: table-cell;
font-size: 1.2em;
}
span[role=button],
button {
cursor: pointer;
background-color: #e1e1e1;
color: #000000;
border: none;
padding: 8px;
margin-bottom: 8px;
}
#media (max-width: 940px) {
#github img {
width: 90px;
height: 90px;
}
}
</style>
</head>
<body>
<div id="signature-pad" class="signature-pad">
<div class="signature-pad--body">
<canvas>
Update your browser to support the canvas element!
</canvas>
</div>
<div class="signature-pad--footer">
<div class="description">Sign above</div>
<div class="signature-pad--actions">
<form action="#" enctype="multipart/form-data">
<label for="fileupload" id="buttonlabel">
</label>
<input type="file" id="fileupload" accept="image/*">
</form>
<div>
<button type="button" class="button clear" data-action="clear">Clear</button>
</div>
</div>
</div>
</div>
<script>
const wrapper = document.getElementById("signature-pad")
const clearButton = wrapper.querySelector("[data-action=clear]")
const changeColorButton = wrapper.querySelector("[data-action=change-color]")
const undoButton = wrapper.querySelector("[data-action=undo]")
const savePNGButton = wrapper.querySelector("[data-action=save-png]")
const saveJPGButton = wrapper.querySelector("[data-action=save-jpg]")
const saveSVGButton = wrapper.querySelector("[data-action=save-svg]")
const canvas = wrapper.querySelector("canvas")
const fileSelector = document.getElementById('fileupload')
// https://medium.com/the-everyday-developer/detect-file-mime-type-using-magic-numbers-and-javascript-16bc513d4e1e
const verifyAndSetPictureAsBackground = (event) => {
const file = event.target.files[0]
const fReader = new FileReader()
fReader.onloadend = (e) => {
if (e.target.readyState === FileReader.DONE) {
const uint = new Uint8Array(e.target.result)
let bytes = []
uint.forEach((byte) => bytes.push(byte.toString(16)))
const hex = bytes.join('').toUpperCase()
if (!(getMimeType(hex) === 'image/png' || getMimeType(hex) === 'image/gif' || getMimeType(hex) === 'image/jpeg')) {
alert('Please choose a picture(.png, .gif, or .jpeg)')
// https://stackoverflow.com/a/35323290/1904223
file = null
fileSelector.value = ''
if (!/safari/i.test(navigator.userAgent)) {
fileSelector.type = ''
fileSelector.type = 'file'
}
}
if (file) {
const dataURL = window.URL.createObjectURL(file)
signaturePad.fromDataURL(dataURL)
}
}
}
fReader.readAsArrayBuffer(file.slice(0, 4))
}
const getMimeType = (signature) => {
switch (signature) {
case '89504E47':
return 'image/png'
case '47494638':
return 'image/gif'
case 'FFD8FFDB':
case 'FFD8FFE0':
case 'FFD8FFE1':
return 'image/jpeg'
default:
return 'Not allowed filetype'
}
}
fileSelector.addEventListener('change', verifyAndSetPictureAsBackground, false)
const signaturePad = new SignaturePad(canvas, {
// It's Necessary to use an opaque color when saving image as JPEG
// this option can be omitted if only saving as PNG or SVG
backgroundColor: 'rgb(255, 255, 255)'
})
// Adjust canvas coordinate space taking into account pixel ratio,
// to make it look crisp on mobile devices.
// This also causes canvas to be cleared.
const resizeCanvas = () => {
// When zoomed out to less than 100%, for some very strange reason,
// some browsers report devicePixelRatio as less than 1
// and only part of the canvas is cleared then.
const ratio = Math.max(window.devicePixelRatio || 1, 1)
// This part causes the canvas to be cleared
canvas.width = canvas.offsetWidth * ratio
canvas.height = canvas.offsetHeight * ratio
canvas.getContext("2d").scale(ratio, ratio)
// This library does not listen for canvas changes, so after the canvas is automatically
// cleared by the browser, SignaturePad#isEmpty might still return false, even though the
// canvas looks empty, because the internal data of this library wasn't cleared. To make sure
// that the state of this library is consistent with visual state of the canvas, you
// have to clear it manually.
signaturePad.clear()
}
// On mobile devices it might make more sense to listen to orientation change,
// rather than window resize events.
window.onresize = resizeCanvas
resizeCanvas()
const download = (dataURL, filename) => {
const blob = dataURLToBlob(dataURL)
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.style = "display: none"
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
}
// One could simply use Canvas#toBlob method instead, but it's just to show
// that it can be done using result of SignaturePad#toDataURL.
function dataURLToBlob(dataURL) {
// Code taken from https://github.com/ebidel/filer.js
const parts = dataURL.split('base64,')
const contentType = parts[0].split(":")[1]
const raw = window.atob(parts[1])
const rawLength = raw.length
const uInt8Array = new Uint8Array(rawLength)
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i)
}
return new Blob([uInt8Array], { type: contentType })
}
clearButton.addEventListener("click", () => signaturePad.clear())
undoButton.addEventListener("click", () => {
const data = signaturePad.toData()
if (data) {
data.pop() // remove the last dot or line
signaturePad.fromData(data)
}
})
changeColorButton.addEventListener("click", () => {
const r = Math.round(Math.random() * 255)
const g = Math.round(Math.random() * 255)
const b = Math.round(Math.random() * 255)
const color = "rgb(" + r + "," + g + "," + b +")"
signaturePad.penColor = color
})
savePNGButton.addEventListener("click", () => {
if (signaturePad.isEmpty()) {
alert("Please provide a signature first.")
} else {
const dataURL = signaturePad.toDataURL()
download(dataURL, "signature.png")
}
})
saveJPGButton.addEventListener("click", () => {
if (signaturePad.isEmpty()) {
alert("Please provide a signature first.")
} else {
const dataURL = signaturePad.toDataURL("image/jpeg")
download(dataURL, "signature.jpg")
}
})
saveSVGButton.addEventListener("click", () => {
if (signaturePad.isEmpty()) {
alert("Please provide a signature first.")
} else {
const dataURL = signaturePad.toDataURL('image/svg+xml')
download(dataURL, "signature.svg")
}
})
</script>
</body>
</html>
When you open this file in the Browser you need to look into the "Developer Tools" to find the error in the console (If you don't know how to do that: try right-clicking on your webpage and select "inspect" from the context menu)
The console will show you:
Uncaught ReferenceError: SignaturePad is not defined
file:test.html:233
Looking in line 233 you find
const signaturePad = new SignaturePad(....
Where is the class SignaturePad defined? Did you maybe leave it in a separate file?
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 days ago.
This post was edited and submitted for review 5 days ago.
Improve this question
I'm trying to make a graphical interface that reads a .json file using javascript+html/css.
I want to display the names (as well as eventually the scores) of the players being fed from the .json file
My code is taken from a full working version that displays names and scores in another html file I feed into OBS software.
However, when calling the text via the updatePlayerName function in js, nothing appears on screen.
What is preventing the player names from being read into the javascript code and then displayed via html file?
//animation stuff
const pMove = 20; //distance to move for most of the animations
const sMove = 40; //distance for the score assets
const fadeInTime = .3; //(seconds)
const fadeOutTime = .2;
let introDelay = .8; //all animations will get this delay when the html loads (use this so it times with your transition)
//max text sizes (used when resizing back)
const roundSize = '32px';
const casterSize = '24px';
window.onload = init();
function init() {
async function mainLoop() {
const scInfo = await getInfo();
getData(scInfo);
}
}
function getData(scInfo) {
let p1Name = scInfo['p1Name'];
let p1Team = scInfo['p1Team'];
let p1Score = scInfo['p1Score'];
let p1Color = scInfo['p1Color'];
let p1Character = scInfo['p1Character'];
let p2Name = scInfo['p2Name'];
let p2Team = scInfo['p2Team'];
let p2Score = scInfo['p2Score'];
let p2Color = scInfo['p2Color'];
let p2Character = scInfo['p2Character'];
let round = scInfo['round'];
let bestOf = scInfo['bestOf'];
let eventName = scInfo['tournamentName'];
updatePlayerName('p1Wrapper', 'p1Name', 'p1Team', p1Name, p1Team);
}
//text resize, keeps making the text smaller until it fits
function resizeText(textEL) {
const childrens = textEL.children;
while (textEL.scrollWidth > textEL.offsetWidth) {
if (childrens.length > 0) { //for team+player texts
Array.from(childrens).forEach(function (child) {
child.style.fontSize = getFontSize(child);
});
} else {
textEL.style.fontSize = getFontSize(textEL);
}
}
}
//returns a smaller fontSize for the given element
function getFontSize(textElement) {
return (parseFloat(textElement.style.fontSize.slice(0, -2)) * .90) + 'px';
}
//color codes here!
function getHexColor(color) {
switch (color) {
case "Red":
return "#e54c4c";
case "Blue":
return "#4b4ce5";
case "Yellow":
return "#ffcb00";
case "Green":
return "#00b200";
case "CPU":
return "#808080";
default:
return "#808080";
}
}
//color code for player text
function getHexColorText(color) {
switch (color) {
case "Red":
return "#F9F6EE";
case "Blue":
return "#F9F6EE";
case "Yellow":
return "#F9F6EE";
case "Green":
return "#F9F6EE";
case "CPU":
return "#F9F6EE";
default:
return "#F9F6EE";
}
}
function getInfo() {
return new Promise(function (resolve) {
const oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", 'Resources/Texts/ScoreboardInfo.json');
oReq.send();
//will trigger when file loads
function reqListener () {
resolve(JSON.parse(oReq.responseText))
}
})
//i would gladly have used fetch, but OBS local files wont support that :(
}
//player text change
function updatePlayerName(wrapperID, nameID, teamID, pName, pTeam) {
const nameEL = document.getElementById(nameID);
nameEL.style.fontSize = '40px'; //set original text size
nameEL.textContent = pName; //change the actual text
resizeText(document.getElementById(wrapperID)); //resize if it overflows
}
//score text change
function updatePlayerScore(wrapperID, scoreID, pScore) {
const scoreChangeEL = document.getElementById(scoreID);
scoreChangeEL.style.fontSize = '35px'; //set original text size
scoreChangeEL.textContent = pScore; //change the actual text
resizeText(document.getElementById(wrapperID)); //resize if it overflows
//searches for the main json file
}
#font-face {
font-family: "Futura";
src: url('Resources/Fonts/Brandon_bld.otf');
}
*{
margin: 0;
padding: 0;
font-family: "Futura";
box-sizing: border-box;
}
.graphicBox{
position: absolute;
width: 1800px;
height: 105px;
background: rgb(51,116,182);
background: linear-gradient(90deg, rgba(51,116,182,0.75) 0%, rgba(110,83,180,0.75) 70%);
border-radius: 15px;
align: center;
bottom: 40px;
left: 50px;
filter: drop-shadow(-2px 2px 4px black);
}
.statusBox{
position: absolute;
font-family: "Futura";
font-weight: bold;
padding-top: 8px;
font-size: 40px;
width: 130px;
height: 70px;
background: rgb(231,231,231);
border-radius: 15px;
text-align: center;
bottom: 125px;
left: 100px;
filter: drop-shadow(-2px 2px 4px black);
}
.timeDisplay{
width: 140px;
height: 20px;
display: flex;
align-items: left;
justify-content: center;
position: absolute;
font-size: 14px;
color: white;
bottom: 70px;
left: 373px;
}
.timeZone{
width: 60px;
height: 20px;
display: flex;
align-items: left;
justify-content: space-between;
position: absolute;
font-size: 15px;
color: white;
bottom: 40px;
left: 412px;
}
.scoreboardP1 {
position: absolute;
left: 70px;
bottom: 20px;
}
.scoreboardP2 {
position: absolute;
right: -40px;
top: 8px;
}
.wrappers {
position: absolute;
width: 340px;
height: 54px;
line-height: 54px;
opacity: 0;
color: white;
}
#p1Wrapper {
bottom: 17px;
left: 50px;
filter: drop-shadow(0px 0px 1px black);
}
#p2Wrapper {
bottom: 17px;
right: 100px;
filter: drop-shadow(0px 0px 1px black);
text-align: right;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="wideth=device-width, initial-scale=1.0", charset="UTF-8">
<title>Current Stream Banner</title>
<script src="Resources/Scripts/infoBanner.js"></script>
<script src="Resources/Scripts/gsap.min.js"></script>
<script src="Resources/Scripts/jquery-3.6.3.slim.min.js"></script>
</head>
<body>
<div class="graphicBox">
</div>
<div class="statusBox">
<p>NEXT</p>
</div>
<div class="timeDisplay">
<h1 id="current-time"></h1>
</div>
<script>
//Time stuff
let time = document.getElementById("current-time");
setInterval(() =>{
let d = new Date();
time.innerHTML = d.toLocaleTimeString(navigator.language, {hourCycle: 'h23', hour: '2-digit', minute:'2-digit'});
},1000);
</script>
<div class="timeZone">
<p>EST</p>
</div>
<div id="p1Wrapper" class="wrappers">
<span id="p1Name" class="names"></span>
</div>
</body>
</html>
I've used notepad++ to write and edit in, as well as jfiddle now to check for any console errors and I am seeing none.
const player = (name, symbol) => {
return { name, symbol };
};
const Player1 = player("Player1", "X");
const Player2 = player("Player2", "O");
console.log(Player1);
for (i = 0; i < 9; i++) {
const getBoard = document.getElementById("board");
const createBoard = document.createElement("div");
getBoard.appendChild(createBoard);
createBoard.className = "game-board";
}
function createPItem(name) {
let p = document.createElement("p");
p.textContent = name;
p.className = "text";
return p;
}
const board = document.querySelector(".game-board");
board.addEventListener("click", selectBoard);
function selectBoard() {
board.appendChild(createPItem(`${Player1.symbol}`));
board.className = "complete-board";
}
body {
background-color: #fffdfa;
}
.title {
font-size: 42px;
font-family: "Dancing Script", cursive;
}
.player-turn {
font-size: 24px;
}
.content {
background-color: #fffdfa;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 700px;
width: 800px;
margin: 0 auto;
border-radius: 8px;
}
.board {
display: flex;
flex-flow: row wrap;
align-content: center;
justify-content: space-evenly;
gap: 10px;
background-color: #fff;
height: 500px;
width: 600px;
}
.board div,
.complete-board {
display: flex;
justify-content: center;
align-items: center;
border: 2px solid #000;
background-color: #3c4048;
width: 175px;
height: 150px;
}
.board div:hover {
background-color: #f4e06d;
transform: scale(1.1);
transition: 0.8s;
box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.5);
}
.complete-board {
background-color: #f4e06d !important;
}
.text {
font-size: 64px;
}
.btn {
display: inline-block;
background-color: #4649ff;
padding: 0.5em 2em;
margin-top: 20px;
cursor: pointer;
font-family: "Poor Story", cursive;
font-size: 2rem;
letter-spacing: 0.4rem;
transition: all 0.3s;
text-decoration: none;
}
.btn:hover {
box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.5);
}
.parallelogram {
transform: skew(-20deg);
}
.skew-fix {
display: inline-block;
transform: skew(20deg);
}
<!DOCTYPE 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="styles.css" />
<title>Tic Tac Toe</title>
</head>
<body>
<div class="content">
<p class="title">Tic Tac Toe</p>
<p class="player-turn">Player X turn</p>
<div id="board" class="board"></div>
<a id="btn" class="btn parallelogram">
<span class="skew-fix">Reset ></span>
</a>
</div>
<script src="app.js"></script>
</body>
</html>
I'm making Tic Tac Toe and I have a function that creates two players, assigning a symbol to each of them.
const player = (name, symbol) => {
return { name, symbol };
};
const Player1 = player("Player1", "X");
const Player2 = player("Player2", "O");
I also have this HTML which creates a board
<div class="content">
<p class="title">Tic Tac Toe</p>
<p class="player-turn">Player X turn</p>
<div id="board" class="board"></div>
</div>
and using a combination of display: flex and this for loop I create 9 divs that fit perfectly inside the game board.
for (i = 0; i < 9; i++) {
const getBoard = document.getElementById("board");
const createBoard = document.createElement("div");
getBoard.appendChild(createBoard);
createBoard.className = "game-board";
}
I have a function that creates a p element & assigns a class to it to get the proper font-size.
function createPItem(name) {
let p = document.createElement("p");
p.textContent = name;
p.className = "text";
return p;
}
And finally an eventListener that selects the gameBoard class that I click on and adds some text.
const board = document.querySelector(".game-board");
board.addEventListener("click", selectBoard);
function selectBoard() {
board.appendChild(createPItem(`${Player1.symbol}`));
board.className = "complete-board";
}
When I click the first div inside my board class, the above code works perfect and creates / assigns the p tag. Inside the p tag is the letter "X" which is what I want to happen. But when I click on any other div, nothing happens and I don't know why.
What I've tried
I've tried a combination of several different things, including changing the for loop to give each div a class name like board${i} but not only does that seem tacky, it didn't actually work and instead filled in every divs textContent all at once.
The issue is because you use querySelector(".game-board"), which will only select the first .game-board element. You need to use querySelectorAll() to find all elements with that class, and then loop over them to add the event handler.
In addition the selectBoard() function needs to be amended to accept the Event object as an argument so you can get a reference to the clicked element without relying on global variables:
const board = document.querySelectorAll(".game-board");
board.forEach(el => el.addEventListener("click", selectBoard));
function selectBoard(e) {
e.target.appendChild(createPItem(`${Player1.symbol}`));
e.target.className = "complete-board";
}
Here's a full working version:
const player = (name, symbol) => ({ name, symbol });
const Player1 = player("Player1", "X");
const Player2 = player("Player2", "O");
for (i = 0; i < 9; i++) {
const getBoard = document.querySelector("#board");
const createBoard = document.createElement("div");
getBoard.appendChild(createBoard);
createBoard.className = "game-board";
}
function createPItem(name) {
let p = document.createElement("p");
p.textContent = name;
p.className = "text";
return p;
}
const board = document.querySelectorAll(".game-board");
board.forEach(el => el.addEventListener("click", selectBoard));
function selectBoard(e) {
e.target.appendChild(createPItem(`${Player1.symbol}`));
e.target.className = "complete-board";
e.target.removeEventListener('click', selectBoard); // prevent additional clicks
}
#board {
display: flex;
flex-wrap: wrap;
width: 150px;
border: 1px solid #000;
}
#board .game-board,
#board .complete-board {
flex: 1 1 30%;
border: 1px solid #000;
height: 50px;
}
#board .complete-board {
background-color: #CCC;
text-align: center;
}
<div id="board" class="board"></div>
I wanted to remove an element created using the JavaScript of specific class when clicked outside the box .
I have used created a color-picker using .createElement . Add a class to it .
Now I wanted to remove that picker when outside of its parent element is clicked as sometimes users don't pick the color and on every click a color-picker is created .
How my function works :
Click on circle option will open up to change : borderColor or backgroundColor
When one option is chosen the color of that property is changed
var restyleBG = document.getElementsByClassName("restyleBackground");
restyleBG[0].addEventListener('click', changeBGcolor);
function changeBGcolor() {
let optionChooseBackground = document.getElementById("optionToChooseBackground");
optionChooseBackground.style.display = "block";
let optionChooseTag = optionChooseBackground.getElementsByTagName("p")
for (let j = 0; j < optionChooseTag.length; j++) {
optionChooseTag[j].onclick = function() {
var varWantToChange = optionChooseTag[j].innerHTML;
let optionToChoosePicker = document.getElementById("optionToChoose");
let colourPicker = document.createElement("input");
colourPicker.type = "color";
colourPicker.className = "colour-picker";
optionToChoosePicker.appendChild(colourPicker);
colourPicker.click();
colourPicker.addEventListener('input', function() {
var colorPickerVal = colourPicker.value;
if (varWantToChange == "borderColor") {
restyleBG[0].style.borderColor = colorPickerVal;
} else if (varWantToChange == "backgroundColor") {
restyleBG[0].style.backgroundColor = colorPickerVal;
}
})
}
}
}
#optionToChoose {
border: 2px solid red;
width: 200px;
}
#optionToChooseBackground {
position: absolute;
height: 100vh;
width: 100vw;
background-color: rgba(165, 42, 42, 0.205);
display: none;
}
#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;
}
<div id="optionToChooseBackground">
<div id="optionToChoose">
<h3>Choose a style want to change :</h3>
<h4>Border</h4>
<p>borderColor</p>
<p>backgroundColor</p>
</div>
</div>
<div id="clockOuterCircle" class="restyleBackground"></div>
Upto now works as needed but creating pickers on each click whether choosing a color or not (not used a on-change event on color picker which executes when color value is changed and remove the picker but this don't come in handy when user just click the property and don't change the color).
The method I tried is in below snippet that when outside of color-picker parent element is clicked it removes the created element but this is throwing error when clicked .
var restyleBG = document.getElementsByClassName("restyleBackground");
restyleBG[0].addEventListener('click', changeBGcolor);
function changeBGcolor() {
let optionChooseBackground = document.getElementById("optionToChooseBackground");
optionChooseBackground.style.display = "block";
let optionChooseTag = optionChooseBackground.getElementsByTagName("p")
for (let j = 0; j < optionChooseTag.length; j++) {
optionChooseTag[j].onclick = function() {
var varWantToChange = optionChooseTag[j].innerHTML;
let optionToChoosePicker = document.getElementById("optionToChoose");
let colourPicker = document.createElement("input");
colourPicker.type = "color";
colourPicker.className = "colour-picker";
optionToChoosePicker.appendChild(colourPicker);
colourPicker.click();
colourPicker.addEventListener('input', function() {
var colorPickerVal = colourPicker.value;
if (varWantToChange == "borderColor") {
restyleBG[0].style.borderColor = colorPickerVal;
} else if (varWantToChange == "backgroundColor") {
restyleBG[0].style.backgroundColor = colorPickerVal;
}
})
optionChooseBackground.addEventListener('click', optionChooseBackgroundClose)
function optionChooseBackgroundClose() {
if (event.target == optionChooseBackground) {
let optionToChoosePicker = document.getElementById("optionToChoose");
optionToChoosePicker.removeChild(colourPicker);
}
}
}
}
}
#optionToChoose {
border: 2px solid red;
width: 200px;
}
#optionToChooseBackground {
position: absolute;
height: 100vh;
width: 100vw;
background-color: rgba(165, 42, 42, 0.205);
display: none;
}
#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;
}
<div id="optionToChooseBackground">
<div id="optionToChoose">
<h3>Choose a style want to change :</h3>
<h4>Border</h4>
<p>borderColor</p>
<p>backgroundColor</p>
</div>
</div>
<div id="clockOuterCircle" class="restyleBackground"></div>
Thank you very much in advance
Any better method to remove picker than above mentioned is most welcomed
Try this.
Edit: I removed new ids. HTML remains as yours.
var restyleBG = document.getElementsByClassName("restyleBackground");
restyleBG[0].addEventListener('click', changeBGcolor);
function changeBGcolor() {
let optionChooseBackground = document.getElementById("optionToChooseBackground");
optionChooseBackground.style.display = "block";
let optionToChoosePicker = document.getElementById("optionToChoose");
let colourPicker = document.createElement("input");
colourPicker.type = "color";
colourPicker.className = "colour-picker";
optionToChoosePicker.appendChild(colourPicker);
let optionChooseTag = optionChooseBackground.getElementsByTagName("p")
var varWantToChange = "";
for (let j = 0; j < optionChooseTag.length; j++) {
optionChooseTag[j].addEventListener("click", () => {
varWantToChange = optionChooseTag[j].innerHTML;
});
}
colourPicker.addEventListener('input', function() {
var colorPickerVal = colourPicker.value;
if (varWantToChange === "borderColor") {
restyleBG[0].style.borderColor = colorPickerVal;
} else if (varWantToChange === "backgroundColor") {
restyleBG[0].style.backgroundColor = colorPickerVal;
}
})
}
#optionToChoose {
border: 2px solid red;
width: 200px;
}
#optionToChooseBackground {
position: absolute;
height: 100vh;
width: 100vw;
background-color: rgba(165, 42, 42, 0.205);
display: none;
}
#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;
}
<div id="optionToChooseBackground">
<div id="optionToChoose">
<h3>Choose a style want to change :</h3>
<h4>Border</h4>
<p>borderColor</p>
<p>backgroundColor</p>
</div>
</div>
<div id="clockOuterCircle" class="restyleBackground"></div>
When you open the datepicker You must create a transparent div that fill the screen but below the selector and add the closing event to it.
I am working on a project which:
Asks the user to choose images from their device
The selected images would show up in the image slider
const slider = document.querySelector('.slider');
// load user chosen files
fileInp.addEventListener('input', (e) =>
{
const files = e.target.files;
for (let file of files) {
const img = new Image();
const reader = new FileReader();
reader.addEventListener('load', (e) =>
{
img.src = e.target.result;
slider.appendChild(img);
img.classList.add('slide');
})
reader.readAsDataURL(file);
}
})
// slider
const slides = document.querySelectorAll('.slide');
let counter = 0;
slides.forEach((slide, index) =>
{
slide.style.left =
`${index * 100}%`
})
nextBtn.onclick = () => {
counter++;
move();
}
prevBtn.onclick = () => {
counter--;
move();
}
function move() {
if (counter <= 0) {
counter = slides.length - 1;
}
if (counter === slides.length) {
counter = 0;
}
slides.forEach((slide, index) => {
slide.style.transform =
`translateX(-${counter * 100}%)`
})
}
#import url("https://fonts.googleapis.com/css2?family=Nunito:wght#400;900");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Nunito", sans-serif;
}
body {
display: grid;
place-items: center;
height: 100vh;
}
.box {
display: flex;
flex-direction: column;
padding: 1rem;
gap: 1rem;
}
.slider {
min-width: 300px;
min-height: 70px;
display: flex;
overflow: hidden;
position: relative;
}
.slide {
position: absolute;
height: 100%;
width: 100%;
object-fit: cover;
object-position: center;
transition: transform 250ms ease;
}
h1 {
margin-bottom: 0.25rem;
}
.controls {
display: flex;
gap: 0.5rem;
width: 100%;
align-items: center;
}
.btn {
background-color: #333;
border: 0;
padding: 0.5rem 0.75em;
width: 12ch;
text-transform: uppercase;
letter-spacing: 1px;
color: #fff;
}
.ph {
background-color: #08b;
width: max-content;
}
.btn:hover {
border: 2px solid #000;
border-radius: .25rem;
}
<div class="box">
<h1>Gallery</h1>
<div class="slider"></div>
<div class="controls">
<button class="btn" id="prevBtn">previous</button>
<input hidden type="file" id="fileInp" multiple />
<!-- Just a placeholder to easily style the file input -->
<button onclick="fileInp.click()" class="ph btn">choose files</button>
<button class="btn" id="nextBtn">next</button>
</div>
</div>
i am adding a snippet so you can understand it clearly,
When using static HTML tags, the slider works properly but when I generate images from file input using JavaScript it gives unwanted results. Where am I going wrong?
it because on page load const slides = document.querySelectorAll('.slide'); is empty, move it to file load event
const slider = document.querySelector('.slider');
let slides = [];
// load user chosen files
fileInp.addEventListener('input', (e) => {
const files = e.target.files;
for (let file of files) {
const img = new Image();
const reader = new FileReader();
reader.addEventListener('load', (e) => {
img.src = e.target.result;
slider.appendChild(img);
img.classList.add('slide');
slides = document.querySelectorAll('.slide');
slides.forEach((slide, index) => {
slide.style.left =
`${index * 100}%`
})
})
reader.readAsDataURL(file);
}
})
// slider
let counter = 0;
nextBtn.onclick = () => {
counter++;
move();
}
prevBtn.onclick = () => {
counter--;
move();
}
function move() {
if (counter <= 0) {
counter = slides.length - 1;
}
if (counter === slides.length) {
counter = 0;
}
slides.forEach((slide, index) => {
slide.style.transform =
`translateX(-${counter * 100}%)`
})
}
.slide {
position: absolute;
height: 100%;
width: 100%;
top: 110px;
object-fit: cover;
object-position: center;
transition: transform 250ms ease;
}
h1 {
margin-bottom: 0.25rem;
}
.controls {
display: flex;
gap: 0.5rem;
width: 100%;
align-items: center;
}
.btn {
background-color: #333;
border: 0;
padding: 0.5rem 0.75em;
width: 12ch;
text-transform: uppercase;
letter-spacing: 1px;
color: #fff;
}
.ph {
background-color: #08b;
width: max-content;
}
.btn:hover {
background-color: #222;
}
<div class="box">
<h1>Gallery</h1>
<div class="slider"></div>
<div class="controls">
<button class="btn" id="prevBtn">previous</button>
<input hidden type="file" id="fileInp" multiple />
<!-- Just a placeholder to easily style the file input -->
<button onclick="fileInp.click()" class="ph btn">choose files</button>
<button class="btn" id="nextBtn">next</button>
</div>
I am trying to take a picture by webcam and I have written codes which work fine but when I coppy them in my project with zend framework 1.11 it does not show any thing and even it does not ask about permission to access to webcam.
I have searched a lot but i am not even sure that is there any problem with zend version or not?
here is my code(i have found it in the internet and copy to one of my phtml files in my project)
<!doctype html>
<style>
.layout1 {
width: 1000px;
margin: 100px auto;
text-align: center;
}
.row1 {
width: 100;
position: relative;
clear: both;
}
.cell1 {
float: right;
display: block;
width: 50%;
}
.center1 {
text-align: center;
}
#newImages {
height: 300px;
position: relative;
width: 100%;
text-align: center;
margin-right: auto;
margin-left: -150px;
}
img.masked {
position: absolute;
background-color: #fff;
border: 1px solid #babbbd;
padding: 10px;
box-shadow: 1px 1px 1px #babbbd;
margin: 10px auto 0;
}
#player {
width: 320px;
height: 240px;
margin: 10px auto;
border: 1px solid #babbbd;
}
canvas {
width: 320px;
height: 240px;
margin: 10px auto;
border: 1px solid #babbbd;
}
#capture-btn {
width: 130px;
margin: 0 auto;
}
#pick-image {
display: none;
text-align: center;
padding-top: 30px;
}
.btn.btn-primary {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: 400;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-image: none;
border: 1px solid transparent;
border-radius: 4px;
color: #fff;
background-color: #337ab7;
border-color: #2e6da4;
</style>
<html>
<head>
<meta charset="utf-8" />
<title>Web app that takes pictures</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="layout">
<div id="newImages"></div>
<div class="row">
<div class="cell">
<video id="player" autoplay></video>
</div>
<div class="cell"></div>
<canvas id="canvas" width="320px" height="240px"></canvas>
</div>
</div>
<div class="center">
<button class="btn btn-primary" id="capture-btn">Capture</button>
</div>
<div id="pick-image">
<label>Video is not supported. Pick an Image instead</label>
<input type="file" accept="image/*" id="image-picker">
</div>
</div>
<!--
<script src="script.js"></script>-->
</body>
</html>
<script>
const videoPlayer = document.querySelector("#player");
const canvasElement = document.querySelector("#canvas");
const captureButton = document.querySelector("#capture-btn");
const imagePicker = document.querySelector("#image-picker");
const imagePickerArea = document.querySelector("#pick-image");
const newImages = document.querySelector("#newImages");
// Image dimensions
const width = 320;
const height = 240;
let zIndex = 1;
const createImage = (src, alt, title, width, height, className) => {
let newImg = document.createElement("img");
if (src !== null) newImg.setAttribute("src", src);
if (alt !== null) newImg.setAttribute("alt", alt);
if (title !== null) newImg.setAttribute("title", title);
if (width !== null) newImg.setAttribute("width", width);
if (height !== null) newImg.setAttribute("height", height);
if (className !== null) newImg.setAttribute("class", className);
return newImg;
};
const startMedia = () => {
if (!("mediaDevices" in navigator)) {
navigator.mediaDevices = {};
}
if (!("getUserMedia" in navigator.mediaDevices)) {
navigator.mediaDevices.getUserMedia = constraints => {
const getUserMedia =
navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error("getUserMedia is not supported"));
} else {
return new Promise((resolve, reject) =>
getUserMedia.call(navigator, constraints, resolve, reject)
);
}
};
}
navigator.mediaDevices
.getUserMedia({ video: true })
.then(stream => {
videoPlayer.srcObject = stream;
videoPlayer.style.display = "block";
})
.catch(err => {
imagePickerArea.style.display = "block";
});
};
// Capture the image, save it and then paste it to the DOM
captureButton.addEventListener("click", event => {
// Draw the image from the video player on the canvas
canvasElement.style.display = "block";
const context = canvasElement.getContext("2d");
context.drawImage(videoPlayer, 0, 0, canvas.width, canvas.height);
videoPlayer.srcObject.getVideoTracks().forEach(track => {
// track.stop();
});
// Convert the data so it can be saved as a file
let picture = canvasElement.toDataURL();
// Save the file by posting it to the server
fetch("./api/save_image.php", {
method: "post",
body: JSON.stringify({ data: picture })
})
.then(res => res.json())
.then(data => {
if (data.success) {
// Create the image and give it the CSS style with a random tilt
// and a z-index which gets bigger
// each time that an image is added to the div
let newImage = createImage(
data.path,
"new image",
"new image",
width,
height,
"masked"
);
console.log(newImage);
let tilt = -(20 + 60 * Math.random());
newImage.style.transform = "rotate(" + tilt + "deg)";
zIndex++;
newImage.style.zIndex = zIndex;
newImages.appendChild(newImage);
canvasElement.classList.add("masked");
}
})
.catch(error => console.log(error));
});
window.addEventListener("load", event => startMedia());
</script>
many thanks