EDIT: 'mouseleave' event is constantly being triggered, although the mouse does not leave the element.
Code works as intended in: chrome, mozilla, edge, opera. But not safari!
I have a vanilla JavaScript solution that changes images every 1000ms when mouse hovered on parent element. There can be any amount of images inside wrapper and this should still work. To be more clear, javascript adds "hidden" class for every image and removes it from the one who's turn is to be displayed. (Code is in fiddle).
In safari it seems to be stuck swapping 2-3rd image. Am I using wrong dom-manipulation approach? How can I find the error?
Problem presentation: https://jsfiddle.net/pcwudrmc/65236/
let imageInt = 0;
let timeOut;
let imagesWrapper = document.querySelectorAll('.items-box__item');
// Events for when mouse enters/leaves
imagesWrapper.forEach(el => {
el.addEventListener('mouseenter', () => startAnim(el));
el.addEventListener('mouseleave', () => stopanim(el));
});
// DOM Manipulation functions
function changeImages(el) {
imageInt += 1;
if (imageInt === el.children[0].children.length) {
// reset to 0 after going through all images
imageInt = 0;
}
for (let i = 0; i < el.children[0].children.length; i++) {
// Adds "hidden" class to ALL of the images for a product
el.children[0].children[i].classList.add('hidden');
}
// Removes "hidden" class for one
el.children[0].children[imageInt].classList.remove('hidden');
// changeImage calls itself again after 1 second, if hovered
timeOut = setTimeout(changeImages.bind(null, el), 1000);
}
function changeBack(el) {
for (let i = 0; i < el.children[0].children.length; i++) {
// Adds "hidden" class to ALL of the images for a product
el.children[0].children[i].classList.add('hidden');
}
// Removes "hidden" class for the first image of the item
el.children[0].children[0].classList.remove('hidden');
}
startAnim = element => { changeImages(element) }
stopanim = element => {
changeBack(element);
clearTimeout(timeOut);
imageInt = 0;
}
.items-box__item {
width: 300px;
height: 300px;
}
.items-box__item--main-image {
object-fit: contain;
width: 90%;
height: 265px;
}
.hidden {
display: none;
}
<h3>Hover on pic and hold mouse</h3>
<div class="items-box__item">
<a href="/">
<img class="items-box__item--main-image" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/yrllszgndxzlydbycewc.jpg"/>
<img class="items-box__item--main-image hidden" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/e96i5zbvxxuxsdczbh9d.jpg"/>
<img class="items-box__item--main-image hidden" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948252/boaqfs3yuc4r7mvhsqqu.jpg"/>
</a>
</div>
You need to look at relatedTarget of mouseleave event, as both mouseenter and mouseleave happen every time the displayed image changes.
Also your code might be simplified. See the snippet below. Hope it helps.
const play = (box) => {
while (!box.classList.contains('items-box__item')) box = box.parentElement;
var img = box.querySelector('.show');
img.classList.remove('show');
(img.nextElementSibling || box.firstElementChild).classList.add('show');
}
const stop = ({target: box, relatedTarget: rt}) => {
while (!box.classList.contains('items-box__item')) box = box.parentElement;
while (rt != box && rt) rt = rt.parentElement;
if (rt === box) return;
box.querySelector('.show').classList.remove('show');
box.firstElementChild.classList.add('show');
box.play = clearInterval(box.play);
}
[...document.querySelectorAll('.items-box__item')]
.forEach((box) => {
box.addEventListener(
'mouseenter',
function() {
if (box.play) return;
play(box);
box.play = setInterval(() => play(box), 1000);
}
);
box.addEventListener('mouseleave', stop);
});
.items-box__item {
display: block;
width: 300px;
height: 300px;
}
.items-box__item img {
object-fit: contain;
width: 90%;
height: 265px;
display: none;
}
img.show {
display: initial
}
<h3>Hover on pic and hold mouse</h3>
<a class="items-box__item" href="/">
<img class="show" src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/yrllszgndxzlydbycewc.jpg">
<img src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948251/e96i5zbvxxuxsdczbh9d.jpg">
<img src="https://res.cloudinary.com/keystone-demo/image/upload/c_limit,h_300,w_300/v1525948252/boaqfs3yuc4r7mvhsqqu.jpg">
</a>
Related
I have an image uploader that I'm building that allows the user to remove images/ image previews prior to form submission. I seem to have a come across a side effect that a) I don't know what is causing it, and b) how to fix it.
When an image is deleted from the preview the image is removed via a click event and the remove() method on the image's parent figure element.
If a user then re-selects the same image (that has just been removed) from their computer with the file picker / input the image doesn't show on the preview the second time around? But, even more confusingly, if a completely different image is attached, and then the user tries to re-attach the original image that previously didn't show the second time, that image does then show again (I hope this makes sense).
I have no idea what is going on here?
Codepen: https://codepen.io/thechewy/pen/qBYYMYV
let dropZone = document.getElementById("zone"),
showSelectedImages = document.getElementById("show-selected-images"),
fileUploader = document.getElementById("upload-files");
dropZone.addEventListener("click", () => {
// assigns the dropzone to the hidden input element, when you click 'select files' it brings up a file picker window
fileUploader.click();
});
// Prevent browser default when draging over
dropZone.addEventListener("dragover", (e) => e.preventDefault());
// Prevent browser default when draging over
dropZone.addEventListener("drop", (e) => e.preventDefault());
fileUploader.addEventListener("change", (e) => {
// this function is further down but declared here and shows a thumbnail of the image
[...fileUploader.files].forEach(updateThumbnail);
console.log("fileUploader.files is: ", [...fileUploader.files]);
});
// updateThumbnail function
function updateThumbnail(file) {
if (file.type.startsWith("image/")) {
let uploadImageWrapper = document.createElement("figure"),
thumbnail = new Image(),
removeImage = `
<div class="remove-image jc-center flex"> Delete </div>
`;
// image thumbnail
thumbnail.classList.add("thumbnail");
thumbnail.src = URL.createObjectURL(file);
// appending elements
showSelectedImages.append(uploadImageWrapper); // <figure> element
uploadImageWrapper.append(thumbnail); // image thumbnail
uploadImageWrapper.insertAdjacentHTML("afterbegin", removeImage); // 'x' to remove image
// get the original width and height values of the thumbnail using the decode() method
thumbnail
.decode()
.then((response) => {
thumbWidth = thumbnail.naturalWidth;
thumbHeight = thumbnail.naturalHeight;
// typical front end image validations
if (thumbWidth * thumbHeight < 4000000) {
// output the error message
}
})
.catch((encodingError) => {
// Do something with the error.
});
// Delete images from the preview
document
.querySelectorAll("#show-selected-images .remove-image")
.forEach((i) => {
i.addEventListener("click", (e) => {
if (e.target) {
var deleteFigureEl = e.target.closest("figure");
// removes the image via removing it's parent element
deleteFigureEl.remove();
}
});
});
}
} // end of 'updateThumbnail' function
body {
margin: 0;
display: flex;
justify-content: center;
font-family: arial;
}
form {
width: 50%;
max-width: 600px;
}
.select-files {
padding: 1rem;
background: red;
cursor: pointer;
color: #fff;
font-weight: bold;
}
#show-selected-images {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 2rem;
}
figure {
width: 100%;
margin: 0;
}
img {
display: block;
width: 100%;
height: auto;
}
.remove-image {
cursor: pointer;
padding: 1rem;
background: lightgrey;
}
<form id="upload-images-form" enctype="multipart/form-data" method="post">
<h1>Upload Your Images</h1>
<div id="zone">
<p class="select-files">SELECT FILES</p>
</div>
<div class="inner-wrapper">
<div class="upload-label-wrapper">
<input id="upload-files" style="display:none;" type="file" name="upload-files[]" multiple>
</div>
<button id="submit-images" oninput="updateThumbnail(this.files)">SUBMIT</button>
</div>
<div id="show-selected-images"></div>
</form>
let dropZone = document.getElementById("zone"),
showSelectedImages = document.getElementById("show-selected-images"),
fileUploader = document.getElementById("upload-files"),
files = [],
filesIndexes = new Set();
dropZone.addEventListener("click", () => {
// assigns the dropzone to the hidden input element, when you click 'select files' it brings up a file picker window
fileUploader.click();
});
// Prevent browser default when draging over
dropZone.addEventListener("dragover", (e) => e.preventDefault());
// Prevent browser default when draging over
dropZone.addEventListener("drop", (e) => e.preventDefault());
fileUploader.addEventListener("change", (e) => {
// this function is further down but declared here and shows a thumbnail of the image
[...fileUploader.files].forEach((file) => {
updateThumbnail(file);
let i = files.length;
while (filesIndexes.has(i)) {
i++;
}
files.push({ file, index: i });
filesIndexes.add(i);
});
fileUploader.value = "";
console.log("fileUploader.files is: ", [...fileUploader.files]);
});
// updateThumbnail function
function updateThumbnail(file) {
if (file.type.startsWith("image/")) {
let uploadImageWrapper = document.createElement("figure"),
thumbnail = new Image(),
removeImage = `
<div class="remove-image jc-center flex" index="${files.length}"> Delete </div>
`;
// image thumbnail
thumbnail.classList.add("thumbnail");
thumbnail.src = URL.createObjectURL(file);
// appending elements
showSelectedImages.append(uploadImageWrapper); // <figure> element
uploadImageWrapper.append(thumbnail); // image thumbnail
uploadImageWrapper.insertAdjacentHTML("afterbegin", removeImage); // 'x' to remove image
// get the original width and height values of the thumbnail using the decode() method
thumbnail
.decode()
.then((response) => {
thumbWidth = thumbnail.naturalWidth;
thumbHeight = thumbnail.naturalHeight;
// typical front end image validations
if (thumbWidth * thumbHeight < 4000000) {
// output the error message
}
})
.catch((encodingError) => {
// Do something with the error.
});
// Delete images from the preview
document
.querySelectorAll("#show-selected-images .remove-image")
.forEach((i) => {
i.onclick = (e) => {
console.log(e.currentTarget);
if (e.target) {
var deleteFigureEl = e.target.closest("figure");
let index = e.currentTarget.getAttribute("index");
files.forEach((obj, i) => {
if (obj.index == index) {
files.splice(i, 1);
filesIndexes.delete(Number(index));
}
});
// removes the image via removing it's parent element
deleteFigureEl.remove();
}
};
});
}
} // end of 'updateThumbnail' function
And then you should upload files like this
function upload(){
let formData = new FormData()
for (let index = 0; index < files.length; index++) {
formData.append(`file${files[index].index}`, files[index].file)
}
fetch("url", {method: "POST", body: formData})
}
I have a file uploader that shows preview images and it is currently set so if you click an image the image preview is deleted and the image is also effectively deleted from the FileList array before the form submission by creating a second array and setting the FileList to hold these values. All of this works OK.
The Context
Whilst setting this up and posting this as a question on StackOverflow in order to (theortically) keep the code simpler I removed the 'remove image' button and set it so the image was deleted both visually and from the FileList if you clicked the image itself. I'm now struggling to work out how to transfer this functionality so when the 'x' is clicked the same functionality happens.
In the current code if you click the image you get the expected behaviour.
I've now included code so that the image preview for each individual image is wrapped in a parent <figure> element with the 'x' .remove-image element included in this too. I've also included some commented out code at the bottom that if uncommented removes the <figure> element containing the image when the 'x' is clicked.
The Question
How do I get it so that instead of when the <img> is clicked the required functionality only happens when the 'x' is clicked?
I would think I need to somehow have the click event on .remove-image (the 'x') that uses the .closest method to go up to the figure element and down to the image i.e. removeImage.closest('figure').querySelector('.img') but I only want the required functionality when the 'x' is clicked, and not when either the 'x' or the image are clicked, which would be simple to fix.
Any help greatly appreciated.
Codepen: https://codepen.io/thechewy/pen/oNdrzjz
let attachFiles = document.getElementById("attach-files");
let previewWrapper = document.getElementById("show-selected-images");
let form = document.getElementById("upload-images-form");
let submitData = new DataTransfer();
attachFiles.addEventListener("change", (e) => {
const currentSubmitData = Array.from(submitData.files);
// For each addded file, add it to submitData if not already present
[...e.target.files].forEach((file) => {
if (currentSubmitData.every((currFile) => currFile.name !== file.name)) {
submitData.items.add(file);
}
});
// Sync attachFiles FileList with submitData FileList
attachFiles.files = submitData.files;
// Clear the previewWrapper before generating new previews
previewWrapper.replaceChildren();
// Generate a preview <img> for each selected file
[...submitData.files].forEach(showFiles);
});
function showFiles(file) {
let uploadImageWrapper = document.createElement("figure");
let previewImage = new Image();
let removeImage = `<div class="remove-image"> X </div>`;
// Set relevant <img> attributes
previewImage.dataset.name = file.name;
previewImage.classList.add("img");
previewImage.src = URL.createObjectURL(file);
// Adds click event listener to <img> preview (this needs moving to the .remove-image element)
previewImage.addEventListener("click", (e) => {
const target = e.currentTarget;
const name = target.dataset.name;
// Remove the clicked file from the submitData
[...submitData.files].forEach((file, idx) => {
if (file.name === name) {
submitData.items.remove(idx);
}
});
// Reset the attachFiles FileList
attachFiles.files = submitData.files;
// Remove the <img> node from the DOM
target.remove();
});
// Append <figure> and <img> preview node to DOM
previewWrapper.append(uploadImageWrapper); // <figure> element
uploadImageWrapper.append(previewImage); // <img>
uploadImageWrapper.insertAdjacentHTML('afterbegin', removeImage); // 'x' to remove the image
// // ===== Delete figure element that wraps the image =====
// document.querySelectorAll('#show-selected-images .remove-image').forEach(i => {
// i.addEventListener('click', (e) => {
// if (e.target) {
// let deleteFigureEl = e.target.closest('figure');
// // removes the image via removing it's parent element
// deleteFigureEl.remove();
// }
// })
// })
}
* {
position: relative;
}
form {
padding: 1rem 2rem;
width: 50%;
border: 1px solid;
}
input,
button {
display: block;
margin: 2rem 0;
}
.remove-image {
background: #000;
width: 30px;
height: 30px;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
top: 10px;
left: -10px;
position: absolute;
z-index: 2;
}
.img {
width: 200px;
height: 200px;
object-fit: cover;
margin: 0 1rem;
}
<form enctype="multipart/form-data" method="post" id="upload-images-form">
<input id="attach-files" type="file" name="attach-files[]" multiple>
<button name="submit" id="submit">SUBMIT</button>
<div id="show-selected-images"></div>
</form>
The two dynamic values in the click listener are the target (which is the previewImage itself) and the name (which is the same as the file.name already in scope). So, all you'd really need to do is change those variable references inside a removeImage event listener (and make removeImage an actual element, not just a string, so that .addEventListener can be called on it).
const removeImage = document.createElement('div');
removeImage.className = 'remove-image';
removeImage.textContent = ' X ';
removeImage.addEventListener('click', () => previewImage.click());
removeImage.addEventListener("click", () => {
const name = file.name;
[...submitData.files].forEach((file, idx) => {
if (file.name === name) {
submitData.items.remove(idx);
}
});
attachFiles.files = submitData.files;
previewImage.remove();
removeImage.remove();
});
let attachFiles = document.getElementById("attach-files");
let previewWrapper = document.getElementById("show-selected-images");
let form = document.getElementById("upload-images-form");
let submitData = new DataTransfer();
attachFiles.addEventListener("change", (e) => {
const currentSubmitData = Array.from(submitData.files);
// For each addded file, add it to submitData if not already present
[...e.target.files].forEach((file) => {
if (currentSubmitData.every((currFile) => currFile.name !== file.name)) {
submitData.items.add(file);
}
});
// Sync attachFiles FileList with submitData FileList
attachFiles.files = submitData.files;
// Clear the previewWrapper before generating new previews
previewWrapper.replaceChildren();
// Generate a preview <img> for each selected file
[...submitData.files].forEach(showFiles);
});
function showFiles(file) {
let uploadImageWrapper = document.createElement("figure");
let previewImage = new Image();
const removeImage = document.createElement('div');
removeImage.className = 'remove-image';
removeImage.textContent = ' X ';
removeImage.addEventListener('click', () => previewImage.click());
removeImage.addEventListener("click", () => {
const name = file.name;
[...submitData.files].forEach((file, idx) => {
if (file.name === name) {
submitData.items.remove(idx);
}
});
attachFiles.files = submitData.files;
previewImage.remove();
removeImage.remove();
});
// Set relevant <img> attributes
previewImage.classList.add("img");
previewImage.src = URL.createObjectURL(file);
// Append <figure> and <img> preview node to DOM
previewWrapper.append(uploadImageWrapper); // <figure> element
uploadImageWrapper.append(previewImage); // <img>
uploadImageWrapper.insertAdjacentElement('afterbegin', removeImage); // 'x' to remove the image
}
* {
position: relative;
}
form {
padding: 1rem 2rem;
width: 50%;
border: 1px solid;
}
input,
button {
display: block;
margin: 2rem 0;
}
.remove-image {
background: #000;
width: 30px;
height: 30px;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
top: 10px;
left: -10px;
position: absolute;
z-index: 2;
}
.img {
width: 200px;
height: 200px;
object-fit: cover;
margin: 0 1rem;
}
<form enctype="multipart/form-data" method="post" id="upload-images-form">
<input id="attach-files" type="file" name="attach-files[]" multiple>
<button name="submit" id="submit">SUBMIT</button>
<div id="show-selected-images"></div>
</form>
cannot fully understand the IntersectionObserver
in the example below, everything works fine, but I'm trying to write only one single observer for multiple entries
and I'm getting various error messages.
Pls, help
let io = new IntersectionObserver((entries)=>{
entries.forEach(entry=>{
if(entry.isIntersecting){navt.classList.remove('navt1');}
else{navt.classList.add('navt1');}
})
})
let io2 = new IntersectionObserver((entries)=>{
entries.forEach(entry=>{
if(entry.isIntersecting){gotopw.style.display = 'block';}
else{gotopw.style.display = 'none';}
})
})
$(document).ready(function(){
io.observe(document.querySelector('#wrapt'));
io2.observe(document.querySelector('#apanel'));
});
Every intersecting entity refers to the element that is intersecting. So to create a single IntersectionObserver you simply have to take advantage of that.
This is a simplified example to show the concept. Note there are two "boxes" that can scroll into view. As they scroll into view the background color changes individually. I used an intersection ratio so you can see the change happen.
The modify() and revert() functions represent operations you would perform in one of the two intersection thresholds.
The test for the element id is the trick that allows the use of one IntersectionObserver for multiple elements.
Scroll slowly to see both boxes.
let io = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && entry.intersectionRatio > 0.5) {
modify(entry.target);
} else {
revert(entry.target);
}
})
}, {
threshold: 0.5
})
function modify(el) {
if (el.id === "wrapt") {
el.style.backgroundColor = 'red';
}
if (el.id === "apanel") {
el.style.backgroundColor = 'green';
}
}
function revert(el) {
if (el.id === "wrapt") {
el.style.backgroundColor = 'initial';
}
if (el.id === "apanel") {
el.style.backgroundColor = 'initial';
}
}
io.observe(document.querySelector('#wrapt'));
io.observe(document.querySelector('#apanel'));
#wrapt {
border: 2px solid black;
height: 100px;
width: 100px;
}
#apanel {
border: 2px solid blue;
height: 100px;
width: 100px;
}
.empty {
height: 400px;
width: 100px;
}
<div class="empty"> </div>
<div id="wrapt">Wrapt</div>
<div class="empty"></div>
<div id="apanel">aPanel</div>
The title may not make the most sense.
Basically I want to store all of the ".mainContent" into X ..
Then when you'd click a button it would make x[0] display none, and bring in x[1].. if that makes sense... that way I could have the same wiping transition and it would just bring in my 4 different pages that I would have.
You can see my page here: I have the "home" and "learn more" pages hard coded, but I obviously don't want to have to do this for every section
http://mikecioffi-me.stackstaging.com/ (*this is not on mobile***) only desktop. mobile version is not ready.. haha
<script>
var dissapearingTime = 1800;
var nextSlideTime = 3000;
var originalNumber = 0;
var newNumber = 1;
var x = $(this).find('.mainContent');
$('.button').hover(
function () {
$('.arrowRight').toggleClass('active');
})
$('.button').click(
function () {
console.log(x);
$('.sidebar').toggleClass('anim-trans');
setTimeout(function () {
$(".hero").css("display", "none");
$(".blank").css("display", "block");
}, dissapearingTime);
setTimeout(function () {
$('.sidebar').toggleClass('anim-trans');
}, nextSlideTime);
})
function hello() {
$('.sidebar').toggleClass('anim-trans');
setTimeout(function () {
$(".blank").css("display", "none");
$(".hero").css("display", "block");
}, dissapearingTime);
setTimeout(function () {
$('.sidebar').toggleClass('anim-trans');
}, nextSlideTime);
}
</script>
i loved your animations :).
I don't know how you use that but you can do with this way. I hope it can help you.
myArray = $('.test');
$(myArray[0]).hide();
https://jsfiddle.net/7rod0vky/2/
one way would be to label each card / div / item with an id, and then you can hide display using a global namespace / json object / dictionary for your own page, that holds some extra info between scopes.
window.__ok = {'current': '', 'prev': null}
var x = $('.main > div');
['#c0', '#c1', '#c2'].map(function(c,i,a){
$(c).on('click', function(a){
window.__ok.prev = window.__ok.current;
window.__ok.current = i;
alert(`show ${c} id: ${i} prev: ${window.__ok.prev}`);
if ($(`#${window.__ok.current}`).css('display') == 'none'){
$(`#${window.__ok.current}`).css('display', 'block');
$(`#${window.__ok.prev}`).css('display', 'none');
}
})
})
.red {
background-color: red;
height: 30px;
display: none
}
.blue {
background-color: blue;
height: 30px;
display: none
}
.green {
background-color: green;
height: 30px;
display: none
}
span {
color: grey;
cursor: pointer
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
<div id="0" class="red">a</div>
<div id="1" class="blue">b</div>
<div id="2" class="green">c</div>
</div>
<span id="c0">show0</span><br>
<span id="c1">show1</span><br>
<span id="c2">show2</span><br>
Here is my fiddle link
I guess my question is clear by title itself. Still, what I am looking for is an way to bind click event on the image added using css's background-image property.
I know, I could have achieved the similar functionality (of placing image over input field using this way or this way) by simply positioning <img> tag over input box and then handling the required events but that way didn't seem too flexible with input fields of varying width and height or if the wrapping div doesn't have position:relative; as its property.
If adding event on image loaded with background-image is not possible then how to make the later approach more flexible.
Hope I have conveyed my issues clearly.
Thanks.
Something like this appears to also work:
$('.cross').click(function(e) {
var mousePosInElement = e.pageX - $(this).position().left;
if (mousePosInElement > $(this).width()) {
$(this).val('');
}
});
Link to example
So you need to bind the click event to the image but not using an embebed attributte that's really a very good and dificult question by the way.
I know my approach is complicated but is the only I can figure right now.
You can get the image size (widthImage and heightImage) and know the position relatives of one to another (here is a way of calculating a position of an objet relative to anohter:jQuery get position of element relative to another element) you may use it to calculate the position of the input in the whole screen
and try something like this:
$(document).ready(function(){
$('body').on('click', 'input.cross', function (e) {
var widthInput = parseInt($(this).css('width'),10);
var heightInput = parseInt($(this).css('height'),10);
var position = $(this).position();
var top = position.top;
var left = position.left;
var sizesRelativesInPercentage = $(this).css('background-size').split(/ +/);
var widthPorcentage = parseInt(sizesRelativesInPercentage[0],10);
var heightPorcentage = parseInt(sizesRelativesInPercentage[1],10);
var widthImage = (widthInput * widthPorcentage)/100;
var heightImage = (heightInput * heightPorcentage)/100;
var xFinalImageFinish= (left+widthInput);
var yFinalImageFinish = (top+heightInput);
// Fire the callback if the click was in the image
if (e.pageX >= xFinalImageStart && e.pageX <= xFinalImageFinish &&
e.pageY >= yFinalImageStart && e.pageY <= yFinalImageFinish) {
// Prevent crazy animations and redundant handling
$(this).off('click');
alert('click on the image');
//Do whatever you want
}
});
});
This is only an idea...I am trying to make a fiddle about this, hope so :O
As pointed out by #Felix King
Since the image is not an element in the document, it does not trigger
events and you cannot bind a handler to it. You have to use a
workaround.
Here is a possible work-around (in jquery, but could just as easily be POJS).
CSS
.wrapper {
padding: 1px;
display: inline-block;
border: 2px LightGray inset;
}
.wrapperFocus {
border: 2px DarkGray inset;
}
.textInput {
padding: 0;
border:none;
outline: none;
height: 20px;
width: 150px;
}
.cross {
float: right;
height: 20px;
width: 20px;
background-image:url('http://s20.postimg.org/6125okgwt/rough_Close.png');
background-repeat:no-repeat;
background-position: center;
background-size:90%;
cursor: pointer;
}
HTML
<fieldset class="wrapper">
<input type="text" class="textInput" /><span class="cross"></span>
</fieldset>
<fieldset class="wrapper">
<input type="text" class="textInput" /><span class="cross"></span>
</fieldset>
<fieldset class="wrapper">
<input type="text" class="textInput" /><span class="cross"></span>
</fieldset>
<hr>
<button>submit</button>
Javascript
$(document).on("click", "span.cross", function () {
$(this).prev().val("");
}).on("focus", "input.textInput", function () {
$(this).parent().addClass("wrapperFocus");
}).on("blur", "input.textInput", function () {
$(this).parent().removeClass("wrapperFocus");
});
On jsfiddle
Or if you want to do it without the additional CSS and HTML, then this should be cross-browser (POJS as you already have a jquery example).
CSS
.cross {
height: 20px;
width: 150px;
background-image:url('http://s20.postimg.org/6125okgwt/rough_Close.png');
background-repeat:no-repeat;
background-position:right center;
background-size:10% 90%;
z-index: -1;
padding-right: 6%;
}
HTML
<input type="text" class="cross" />
<input type="text" class="cross" />
<input type="text" class="cross" />
<hr>
<button>submit</button>
Javascript
function normalise(e) {
e = e || window.event;
e.target = e.target || e.srcElement;
return e;
}
var getWidth = (function () {
var func;
if (document.defaultView.getComputedStyle) {
func = document.defaultView.getComputedStyle;
} else if (target.currentStyle) {
func = function (t) {
return t.currentStyle;
}
} else {
func = function () {
throw new Error("unable to get a computed width");
}
}
return function (target) {
return parseInt(func(target).width);
};
}());
function isInputCross(target) {
return target.tagName.toUpperCase() === "INPUT" && target.className.match(/(?:^|\s)cross(?!\S)/);
}
function isOverImage(e) {
return (e.clientX - e.target.offsetLeft) > getWidth(e.target);
}
function clearCrossInput(e) {
e = normalise(e);
if (isInputCross(e.target) && isOverImage(e)) {
e.target.value = "";
}
}
document.body.onclick = (function () {
var prevFunc = document.body.onclick;
if ({}.toString.call(prevFunc) === "[object Function]") {
return function (ev) {
prevFunc(ev);
clearCrossInput(ev);
};
}
return clearCrossInput;
}());
On jsfiddle
But if you want the cursor to change when hovered over the position then you will need to do some extra work. Like this (you could just as easily do this with jquery too).
Javascript
function hoverCrossInput(e) {
e = normalise(e);
if (isInputCross(e.target)) {
if (isOverImage(e)) {
e.target.style.cursor = "pointer";
return;
}
}
e.target.style.cursor = "";
}
document.body.onmousemove = (function () {
var prevFunc = document.body.onmousemove;
if ({}.toString.call(prevFunc) === "[object Function]") {
return function (ev) {
prevFunc(ev);
hoverCrossInput(ev);
};
}
return hoverCrossInput;
}());
On jsfiddle
I do something similar by using an <input type="text" ... > immediately followed by an <input type="button">
The button is given a background image and positioned-relative to move it into the text field; essentially...
position: relative;
top: 4px;
right: 1.6em;
height: 16px;
width: 16px;
border: none;
Then I just add a dead-plain click handler to the button, no computations necessary.
The height x width would depend on your image, and you would tweak the top and right to fit your situation.
This fiddle shows it pared down to the bare essentials.