Simulate drag and drop of file to upload in Protractor - javascript

I want to test file upload, by dragging file to the drop zone in the page, however I can't find a way to simulate file dragging from the desktop folder.
The only way I managed to found is the following one -
desktop.browser.actions().dragAndDrop(elem,target).mouseUp().perform();(Protractor)
However as far as I can understand, it drags only css element.

This is a working example to simulate a file drop from the desktop to a drop area:
const dropFile = require("./drop-file.js");
const EC = protractor.ExpectedConditions;
browser.ignoreSynchronization = true;
describe('Upload tests', function() {
it('should drop a file to a drop area', function() {
browser.get('http://html5demos.com/file-api');
// drop an image file on the drop area
dropFile($("#holder"), "./image.png");
// wait for the droped image to be displayed in the drop area
browser.wait(EC.presenceOf($("#holder[style*='data:image']")));
});
});
The content of drop-file.js :
var fs = require('fs');
var path = require('path');
var JS_BIND_INPUT = function (target) {
var input = document.createElement('input');
input.type = 'file';
input.style.display = 'none';
input.addEventListener('change', function () {
target.scrollIntoView(true);
var rect = target.getBoundingClientRect(),
x = rect.left + (rect.width >> 1),
y = rect.top + (rect.height >> 1),
data = { files: input.files };
['dragenter','dragover','drop'].forEach(function (name) {
var event = document.createEvent('MouseEvent');
event.initMouseEvent(name, !0, !0, window, 0, 0, 0, x, y, !1, !1, !1, !1, 0, null);
event.dataTransfer = data;
target.dispatchEvent(event);
});
document.body.removeChild(input);
}, false);
document.body.appendChild(input);
return input;
};
/**
* Support function to drop a file to a drop area.
*
* #view
* <div id="drop-area"></div>
*
* #example
* dropFile($("#drop-area"), "./image.png");
*
* #param {ElementFinder} drop area
* #param {string} file path
*/
module.exports = function (dropArea, filePath) {
// get the full path
filePath = path.resolve(filePath);
// assert the file is present
fs.accessSync(filePath, fs.F_OK);
// resolve the drop area
return dropArea.getWebElement().then(function (element) {
// bind a new input to the drop area
browser.executeScript(JS_BIND_INPUT, element).then(function (input) {
// upload the file to the new input
input.sendKeys(filePath);
});
});
};

You cannot drag an element from your desktop using protractor, its actions are limited to the browser capabilities.
You may have to consider dragging from the desktop to work (unless you want to test your operating system), and check that once the file is given to the HTML element, everything works correctly.
One way to achieve that is with the following:
dropElement.sendKeys(path);
For instance if that element is, as usual, an input of type file:
$('input[type="file"]').sendKeys(path);
Note that path should be the absolute path to the file you want to upload, such as /Users/me/foo/bar/myFile.json or c:\foo\bar\myFile.json.

Related

How can I use cropme.js to allow users upload their profile image?

This is a knowledge sharing Q&A.
Cropme is a nice JS add-on for cropping and rotating an image using visual sliders. The author provided good documentation, but building a working implementation is not as simple as it should be.
The question I want to answer is this:
I want to allow my website-users to upload their profile image. That image must be exactly 240x292 pixels. The users should be able zoom and rotate their image, then crop it to that specific size and upload it to my website. How can I do all that with cropme?
These are the requires steps:
Show an empty placeholder for the image we want the user to load.
By clicking the "Get Image" button, the user can select an image from its local files.
The selected file is loaded into memory, and presented for editing using 'cropme'. The user can use visual sliders to rotate and zoom in/out
After clicking "Crop", the user is presented with the cropped image, and can decide to save the image or to cancel.
After clicking "Save", the cropped image is uploaded to a PHP server, the modal window is closed, and the placeholder image is replaced with the link to the just-uploaded image.
So how can we do this?
A fully working demo is presented here:
https://codepen.io/ishahak/pen/XWjVzLr
I will explain some of the details, step by step.
Note: usually in my code when you see obj[0], it is simply a conversion from jQuery object into a simple JS object.
1. Showing a placeholder for the image.
We can create a real SVG image on the fly using this code:
getImagePlaceholder: function(width, height, text) {
//based on https://cloudfour.com/thinks/simple-svg-placeholder/
var svg = '\
<svg xmlns="http://www.w3.org/2000/svg" width="{w}" \
height="{h}" viewBox="0 0 {w} {h}">\
<rect fill="#ddd" width="{w}" height="{h}"/>\
<text fill="rgba(0,0,0,0.5)" font-family="sans-serif"\
font-size="30" dy="10.5" font-weight="bold"\
x="50%" y="50%" text-anchor="middle">{t}</text>\
</svg>';
var cleaned = svg
.replace(/{w}/g, width)
.replace(/{h}/g, height)
.replace('{t}', text)
.replace(/[\t\n\r]/gim, '') // Strip newlines and tabs
.replace(/\s\s+/g, ' ') // Condense multiple spaces
.replace(/'/gim, '\\i'); // Normalize quotes
var encoded = encodeURIComponent(cleaned)
.replace(/\(/g, '%28') // Encode brackets
.replace(/\)/g, '%29');
return 'data:image/svg+xml;charset=UTF-8,' + encoded;
}
2. By clicking the "Get Image" button, the user can select an image from its local files.
This process involves an input element of type "file" which has no visible appearance (we set it with the 'd-none' class), and a button element which 'clicks' it to open a dialog:
<button id="btnGetImage" class="btn btn-primary">Get Image</button>
<input class="d-none" type="file" id="fileUpload" accept="image/*" />
And the relevant code:
$('#btnGetImage').on('click', function(){
//force 'change' event even if repeating same file:
$('#fileUpload').prop("value", "");
$('#fileUpload').click();
});
$('#fileUpload').on('change', function(){
CiM.read_file_from_input(/*input elem*/this, function() {
console.log('image src fully loaded');
$('#imgModal-dialog').modal('show');
});
});
When a file is selected, the 'change' event is firing, leading us to read the file into memory.
3. The selected file is loaded into memory, and presented for editing using 'cropme'. The user can use visual sliders to rotate and zoom in/out
Our read_file_from_input mentioned above is implemented like this:
imgHolder: null,
imgHolderCallback: null,
read_file_from_input: function(input, callback) {
if (input.files && input.files[0]) {
imgHolderCallback = callback;
var reader = new FileReader();
if (!CiM.imgHolder) {
CiM.imgHolder = new Image();
CiM.imgHolder.onload = function () {
if (imgHolderCallback) {
imgHolderCallback();
}
}
}
reader.onload = function (e) {
console.log('image data loaded!');
CiM.imgHolder.src = e.target.result; //listen to img:load...
}
reader.readAsDataURL(input.files[0]);
}
else {
console.warn('failed to read file');
}
}
When the FileReader is ready, we set the src for our internal image holder, and wait for the 'load' event, which signals that the img element is ready with the new content.
We listen to that 'load' event, and when triggered we show the modal. A modal in Bootstrap has several events. We listen to the one which signals that the modal is shown, meaning that the width and set and we can plan our Cropme dimensions based on it.
update_options_for_width: function(w) {
var o = CiM.opt, //shortcut
vp_ratio = o.my_final_size.w / o.my_final_size.h,
h, new_vp_w, new_vp_h;
w = Math.floor(w * 0.9);
h = Math.floor(w / o.my_win_ratio);
o.container.width = w;
o.container.height = h;
new_vp_h = 0.6 * h;
new_vp_w = new_vp_h * vp_ratio;
// if we adapted to the height, but it's too wide:
if (new_vp_w > 0.6 * w) {
new_vp_w = 0.6 * w;
new_vp_h = new_vp_w / vp_ratio;
}
new_vp_w = Math.floor(new_vp_w);
new_vp_h = Math.floor(new_vp_h);
o.viewport.height = new_vp_h;
o.viewport.width = new_vp_w;
}
We wait for the size of the modal to be set because cropme must be set with specific viewport dimensions. At the end of our shown.bs.modal handler, we create our Cropme instance.
4. After clicking "Crop", the user is presented with the cropped image, and can decide to save the image or to cancel.
Here is the save-button handler:
$('#imgModal-btnSave').on('click', function(){
uploadImage(croppedImg[0], function(path_to_saved) {
savedImg[0].src = path_to_saved;
$('#imgModal-dialog').modal('hide');
});
});
The uploadImage function goes like this:
uploadImage: function(img, callback){
var imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d");
// Make sure canvas is as big as the picture (needed??)
imgCanvas.width = img.width;
imgCanvas.height = img.height;
// Draw image into canvas element
imgContext.drawImage(img, 0, 0, img.width, img.height);
var dataURL = imgCanvas.toDataURL();
$.ajax({
type: "POST",
url: "save-img.php", // see code at the bottom
data: {
imgBase64: dataURL
}
}).done(function(resp) {
if (resp.startsWith('nok')) {
console.warn('got save error:', resp);
} else {
if (callback) callback(resp);
}
});
}
It is matched with a simple PHP script which appears at the end of the HTML in the codepen. I think this answer went too long, so I'll finish here.
Good luck - have fun :)

Jcrop does not refresh newly selected image

I work a form that would let users to select an image on their computer, crop it and finally upload as profile image. I use JCrop to crop the photo. After a lot of struggles, now everything seems to work fine, except that if the user changes its mind and would like to browse in a second image, the image is not replaced on my form. I do replace the src attribute of the 'preview' image, but it seems that somehow JCrop still stores the previous image and this somehow "covers" the new image. I tried to use the JCrop setImage method to replace the image, but it does not seem to work. Any hints on how to fix this? Please see the code samples below.
var currentFile;
var file;
var options;
// convert bytes into friendly format
function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB'];
if (bytes == 0) return 'n/a';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
};
// check for selected crop region
function checkForm() {
if (parseInt($('#w').val())) return true;
jQuery('.error').html('Please select a crop region and then press Upload').show();
return false;
};
// update info by cropping (onChange and onSelect events handler)
function updateInfo(e) {
jQuery('#x1').val(e.x);
jQuery('#y1').val(e.y);
jQuery('#x2').val(e.x2);
jQuery('#y2').val(e.y2);
jQuery('#w').val(e.w);
jQuery('#h').val(e.h);
};
// clear info by cropping (onRelease event handler)
function clearInfo() {
jQuery('.info #w').val('');
jQuery('.info #h').val('');
};
function fileSelectHandler(evt) {
// hide all errors
jQuery('.error').hide();
// grabbing image
evt.preventDefault()
var target = evt.target
file = target && target.files && target.files[0]
if (!file) {
jQuery('.error').html('Image not found.').show();
jQuery('#main_photo').val('');
return;
}
var oFile = target.files[0];
// check for image type (jpg is allowed)
var rFilter = /^(image\/jpeg)$/i;
if (! rFilter.test(oFile.type)) {
jQuery('.error').html('Please select a valid image file (jpg)').show();
jQuery('#main_photo').val('');
return;
}
// check for file size
if (oFile.size > 5 * 1024 * 1024) {
jQuery('.error').html('You have selected too big file (max: 5 MB), please select a one smaller image file').show();
jQuery('#main_photo').val('');
return;
}
//setting options for image loader
options = {
orientation: true,
maxWidth: 400
}
// preview element
var oImage = document.getElementById('preview');
// adding onload event handler to initialize JCrop
oImage.onload = function () {
// Create variables (in this scope) to hold the Jcrop API and image size
var jcrop_api, boundx, boundy;
console.log(oImage);
// destroy Jcrop if it is existed
if (typeof jcrop_api != 'undefined') {
jcrop_api.destroy();
jcrop_api = null;
}
// initialize Jcrop
jQuery('#preview').Jcrop({
minSize: [32, 32], // min crop size
aspectRatio : 200/250, // keep aspect ratio 1:1
bgFade: true, // use fade effect
bgOpacity: .3, // fade opacity
trueSize: [oImage.naturalWidth,oImage.naturalHeight],
onChange: updateInfo,
onSelect: updateInfo,
onRelease: clearInfo
}, function(){
// use the Jcrop API to get the real image size
var bounds = this.getBounds();
boundx = bounds[0];
boundy = bounds[1];
// Store the Jcrop API in the jcrop_api variable
jcrop_api = this;
console.log(jQuery('#preview').attr('src'));
jcrop_api.setImage(jQuery('#preview').attr('src'));
});
};
displayImage(file, options);
}
/**
* Updates the results view
*
* #param {*} img Image or canvas element
* #param {object} [data] Meta data object
*/
function updateResults(img, data) {
var fileName = currentFile.name
var href = img.src
var dataURLStart
var content
if (!(img.src || img instanceof HTMLCanvasElement)) {
content = jQuery('<span>Loading image file failed</span>')
} else {
if (!href) {
href = img.toDataURL(currentFile.type + 'REMOVEME')
// Check if file type is supported for the dataURL export:
dataURLStart = 'data:' + currentFile.type
if (href.slice(0, dataURLStart.length) !== dataURLStart) {
fileName = fileName.replace(/\.\w+$/, '.png')
}
}
content = jQuery('<a target="_blank">')
.append(img)
.attr('download', fileName)
.attr('href', href)
}
jQuery('#preview').attr("src", href);
}
/**
* Displays the image
*
* #param {File|Blob|string} file File or Blob object or image URL
* #param {object} [options] Options object
*/
function displayImage(file, options) {
currentFile = file
if (!loadImage(file, updateResults, options)) {
jQuery('.error').html('Incompatible browser. Image cannot be displayed.').show();
jQuery('#main_photo').val('');
return;
}
}
I created a wrapper like this
<div id="wrapper_img">
<img id="img">
</div>
and clean de wrapper and recreate de img to reload new image
$('#wrapper_img').empty();
var img = $('<img id="img">');
img.attr('src', 'my_image.png');
img.appendTo('#wrapper_img');
after that y create a new JCrop
img.Jcrop({....})

(Vis.js network) Incorrect node coordinates with getPositions?

In my vis.js network, I want to make a popup appear at the position of a node when I click on the node.
I used the getPositions method but the popup appears in the wrong place (too much on the left and top corner), as if the coordinates were incorrect.
network.on("click", function (params) {
// Get the node ID
var nodeId = params.nodes[0];
if (nodeId) {
// Get the node title to show in the popup
var popup = this.body.nodes[nodeId].options.title;
// Get the node coordinates
var nodePosition = network.getPositions(nodeId);
var nodeX = nodePosition[nodeId].x;
var nodeY = nodePosition[nodeId].y;
// Show the tooltip in a div
document.getElementById('popup').innerHTML = popup;
document.getElementById('popup').style.display = "block";
// Place the div
document.getElementById('popup').style.position = "absolute";
document.getElementById('popup').style.top = nodeY+'px';
document.getElementById('popup').style.left = nodeX+'px';
}
});
// Empty and hide the div when click elsewhere
network.on("deselectNode", function (params) {
document.getElementById('popup').innerHTML = null;
document.getElementById('popup').style.display = "none";
});
I got some help on the vis support section of github.
Turns out the trick was to use canvasToDOM().
Here's how it applied to my code:
network.on("click", function(params) {
// Get the node ID
var nodeId = params.nodes[0];
if (nodeId) {
// Get the node title to show in the popup
var popup = this.body.nodes[nodeId].options.title;
// Get the node coordinates
var { x: nodeX, y: nodeY } = network.canvasToDOM(
network.getPositions([nodeId])[nodeId]
);
// Show the tooltip in a div
document.getElementById("popup").style.display = "block";
// Place the div
document.getElementById("popup").style.position = "absolute";
document.getElementById("popup").style.top = nodeY + "px";
document.getElementById("popup").style.left = nodeX + "px";
}
});
It works when the network stays put, but in my case I want to fit the network and zoom on the clicked node, so the popup doesn't follow, but since this is a separate issue I will post another question.
You are using network.getPositions(params.nodes[0]), but since the nodes can change a lot when zooming in and out and moving the network on the canvas somehow the positions do not match the real position of the node. I do not know if this is a bug in visjs or there is some other reason for it. The docs say they return the position of the node in the canvas space. But thats clearly not the case in your example.
Looking at the docs [ https://visjs.github.io/vis-network/docs/network/#Events ] the click event argument contains:
{
nodes: [Array of selected nodeIds],
edges: [Array of selected edgeIds],
event: [Object] original click event,
pointer: {
DOM: {x:pointer_x, y:pointer_y}, // << Try using this values
canvas: {x:canvas_x, y:canvas_y}
}
}
Try to use the params.pointer.DOM or params.pointer.canvas positions X and Y to position your popup. This should be the location of the cursor. This will be the same position as the node, since you clicked on it.
So try something like:
document.getElementById('popup').style.top = params.pointer.DOM.y +'px';
document.getElementById('popup').style.left = params.pointer.DOM.x +'px';
-- Untested
use the click event and paint a div over the canvas.
network.on("click", function(params) {
// Get the node ID
var nodeId = params.nodes[0];
if (nodeId) {
// Get the node title to show in the popup
var popup = this.body.nodes[nodeId].options.title;
//use JQUERY to see where the canvas is on the page.
var canvasPosition = $('.vis-network').position();
//the params give x & y relative to the edge of the canvas, not to the
//whole document.
var clickX = params.pointer.DOM.x + canvasPosition.top;
var clickY = params.pointer.DOM.y + canvasPosition.left;
// Show the tooltip in a div
document.getElementById("popup").style.display = "block";
// Place the div
document.getElementById("popup").style.position = "absolute";
document.getElementById("popup").style.top = clickY + "px";
document.getElementById("popup").style.left = clickX + "px";
}
});
fixed position for tooltip/popup example

Javascript Pdf navigation for pdf.js

Is it possibile to use pdf.js to render a PDF in a html page and manage its navigation?
For navigation I mean zoom and pan. My goal is to add some button with pan (up, down, left, right) for zoom (zoom in ,zoom out, default zoom) and, if it's possibile, to automatically pan the document centering it in the click position.
The whole point of pdf.js is to render .pdf documents in HTML5. So what you're saying should be achievable. The project is quite well documented on Github and should be a good starting point. As far as the navigation and other toggle buttons for zooming and paning are concerned, they have been clearly implemented here and here.
So you could start by viewing the source of these demos first and then go through the documentation to fit your needs. Hope this gets you started in the right direction. I'm posting some relevant code snippet from one of the demos:
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 0.8,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');
/**
* Get page info from document, resize canvas accordingly, and render page.
* #param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function () {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Update page counters
document.getElementById('page_num').textContent = pageNum;
}
/**
* If another page rendering in progress, waits until the rendering is
* finised. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
...
/**
* Asynchronously downloads PDF.
*/
PDFJS.getDocument(url).then(function (pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
}

Mouse event with overlapping divs

I need to show a image upon another, than i use the these codes, but when my mouse is over the lisdel that is the list that shows the image, it desappear because it receives the mouseout event. It's VERY hard to explain, but try debuggin it and move your mouse in the image that you will see it.
<script>
var mouseOverListDel = false;
// Detect if the browser is IE or not.
// If it is not IE, we assume that the browser is NS.
var IE = document.all ? true : false
// If NS -- that is, !IE -- then set up for mouse capture
if (!IE) document.captureEvents(Event.MOUSEMOVE)
// Set-up to use getMouseXY function onMouseMove
document.onmousemove = getMouseXY;
// Temporary variables to hold mouse x-y pos.s
var tempX = 0
var tempY = 0
// Main function to retrieve mouse x-y pos.s
function getMouseXY(e) {
if (IE) { // grab the x-y pos.s if browser is IE
tempX = event.clientX + document.body.scrollLeft
tempY = event.clientY + document.body.scrollTop
} else { // grab the x-y pos.s if browser is NS
tempX = e.pageX
tempY = e.pageY
}
// catch possible negative values in NS4
if (tempX < 0) { tempX = 0 }
if (tempY < 0) { tempY = 0 }
// show the position values in the form named Show
// in the text fields named MouseX and MouseY
var txbX = document.getElementById('TextBox1');
var txbY = document.getElementById('TextBox2');
txbX.value = tempX;
return true
}
function getPosition(element) {
var xPosition = 0;
var yPosition = 0;
while (element) {
xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
element = element.offsetParent;
}
return { x: xPosition, y: yPosition };
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function (theFile) {
return function (e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
span.style.height = "65px";
span.style.width = "90px";
document.getElementById('list').insertBefore(span, null);
var del = document.createElement('del');
del.style.visibility = "hidden";
del.innerHTML = ['<img class="thumbdel" src="http://s7.postimage.org/fc6w3qjp3/del.png',
'" title="', escape(theFile.name + "del"), '"/>'].join('');
document.getElementById('listdel').insertBefore(del, null);
del.addEventListener("click", function () { delClick(del, span) }, false);
del.addEventListener('mouseover', function () { opacityOn(del) }, false)
del.addEventListener('mouseout', function () { opacityOn(del) }, false);
span.addEventListener('mouseover', function () { opacityOn(del) }, false);
span.addEventListener('mouseout', function () { opacityOff(del) }, false);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
function delClick(imgDel, img)
{
var listImg = document.getElementById('list');
listImg.removeChild(img);
var listDelImg = document.getElementById('listdel');
listDelImg.removeChild(imgDel);
}
function opacityOn(imgDel)
{
imgDel.style.visibility = "visible";
}
function opacityOff(imgDel)
{
imgDel.style.visibility = "hidden";
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
Could you use CSS for this? Like :
.listDel {
background: none;
}
.listDel :hover {
background: URL("URL to image");
}
Of if you want to do a complex condition for the image to display in javascript why not think of it something like this :
var listdel = document.getElementById('listdel');
listdel.addEventListener('mouseover',
function () { window.mouseOverListDel = true; }
, false);
listdel.addEventListener('mouseout',
function () {
setTimeout(
function () { window.mouseOverListDel = false; }
, 333
);
}
, false);
And then in your opacityOn function (which presumably also hides the delete button image?) check if that flag is set (mouseOverListDel) and if it is, then you don't want to hide the del button image, since you know that the mouse is over the list of delete images, and it should not hide anything.
Even if I have not totally understood your details, this pattern will help. Basically, you want to continue showing an image, even when locally the mouse departs that image's boundary, but it is still in a 'user relevant' location to that image -- i.e., it still "looks pretty close to that image" so it is helpful if we continue to display the image, and unhelpful if we don't.
You could use a library like hoverIntent and use jQuery which makes things easier for this, or you can code it yourself, as I have given you an example of. The basic idea has two parts :
Set a flag for regions you are interested in when the mouse is over them, unset it when the mouse is not over them.
Check these flags from your other mouse over event handlers, to determine if the conditions you choose for the action (image display, image hide, or anything else) are met.
Here's the clincher because of slight variations in the time of firing of the event, you will need to delay the checking of the flag slightly, by a small part of a second (you can test these millisecond values out). So you will need to delay the handler code in mouseout by say 333ms because the, for example, listdel mouseover even may not have yet fired when the del mouseout event fires and your code is executed.
Also : For extra points, these delays and conditions can be used to give you a more smooth UI. Maybe you allow some tolerance when a user, through their random meandering movements, slightly leaves the area of interest for you to display the image, but then comes back, within say 500ms -- if you delay the checking of flags, and the mouseout handlers, you can tolerate this kind of accidental exit. But that part of the UI design is just up to what is useful for you.
Also this line could be causing a problem :
del.style.visibility = "hidden";
Do you ever set it back to visible ? If not then your del will not show. opacity is not the same as visibility.

Categories