How can I implement a callback after layer.destroyChilden() - javascript

This is for homework and it has an annoying bug I can't solve after hours of struggle.
http://canvaseu.chrisloughnane.net/
When on the eu map clicking a country path fires the bound event.
This event destroys the layers children and loads the country. BUT if you click and quickly move the mouse and let go the button the country loads but the country path from the eu remains. This is best demonstrated with Spain as shown in the screen grab.
I am hoping a callback after mapLayer.destroyChildren(); to then call the load function would solve my problem.
This can be a little difficult to replicate.
I'm sure my control is too tied up with my view but I haven't been able to see a solution to separate them neatly.
**** EDIT ****
I came up with a solution that works partially but I think this is terrible hack code, I added to the mousedown binding down = true; and added checks to the mouseout binding, please see below.
What I think is happening is when you move the mouse and let the button go very quickly the mouseover binding is over riding the mouseup.
This solution isn't ideal, after loading a number of countries and mouseover on regions the canvas response slows down.
Event Binding
path.on('mousedown touchstart', function()
{
down = true;
this.setStroke('red');
this.moveTo(topLayer);
/****
* Handle if the path we are displaying in canvas is the eu
* to allow selection and load of country path point data.
*/
if (associativeCountryArray[lastCountry].getText() == 'eu')
{
associativeCountryArray[lastCountry].setFill('#bbb');
associativeCountryArray[lastCountry].setFontStyle('normal');
countrynames[lastCountry].selected = false;
this.moveTo(mapLayer);
mapLayer.destroyChildren();
lastCountry = this.getName();
countrynames[this.getName()].selected = true;
associativeCountryArray[this.getName()].setFill("rgb(" + r + "," + g + "," + b + ")");
associativeCountryArray[this.getName()].setFontStyle('bold');
loadPaths(this.getName().replace(/\s/g, ''));
countryNameLayer.draw();
}
else
{
window.open('https://www.google.com/search?q=' + this.getName(),'_blank');
}
topLayer.drawScene();
});
path.on('mouseout', function()
{
if(!down)
{
document.body.style.cursor = 'default';
this.setFill('#eee');
this.setStrokeWidth(settings.strokewidthstart);
/****
* On hover, only change colour of country names around edge of canvas if we are on the 'eu' map
*/
if (lastCountry == 'eu')
{
associativeCountryArray[this.getName()].setFill('#bbb');
associativeCountryArray[this.getName()].setFontStyle('normal');
}
this.setStroke('#555');
this.moveTo(mapLayer);
writeMessage('');
topLayer.draw();
countryNameLayer.draw();
}
else
{
down = false;
}
});
path.on('mouseup touchend', function()
{
this.setStroke('black');
topLayer.drawScene();
down = false;
});

Like this:
Send the container (group,layer) you want to clear and the callback you want triggered.
function myDestroyChildren(container,callback) {
var children = container.getChildren();
while(children.length > 0) {
children[0].destroy();
}
callback();
}

Related

Event on back elements doesn't trigger in jQuery

Alright, I have a chess board with all the spots and pieces. It's generated onload with two four cycles from a 2D array. The idea is, that you should be able to drag a chess piece (something I've already implemented correctly), and let it go in a valid position. The letting go part should trigger an event in the actual board spots. So each of the 64 spots have an even listener that looks like this.
element.on("mouseup",function(e){
if(Game.current==undefined||!$(this).hasClass("valid")){return;}
var col,row;
col = $(this).attr("column");
row = $(this).attr("row");
Game.current.attr({
"column":col,
"row":row
});
Game.move(Game.current);
Game.changeTurn();
Game.current = undefined;
});
The function triggers normally when I click on a square in the board, however when I drag and drop a chess piece there, it doesn't register that the mouse is up. It let's go of the piece, however since the cursor is directly over the chess piece and said piece "blocks" the cursor from being directly above the spot. It is also worth noting that my HTML structure is like this.
<board>
<spot></spot>
<piece></piece>
</board>
By which I mean, that the pawns, bishop etc. aren't inside the spots, so the event doesn't have anything to "bubble" through.
A better approach would be attach the listener of mouseup to the window object doing something like this
$(window).on('mouseup', on_mouse_up );
and then, from the mouse position calculating the right spot.
function on_mouse_up( e ) {
// Get the mouse position
x = e.pageX;
y = e.pageY;
// Calculate the spot from the mouse position
}
(function ($) {
"use strict";
//console.log($);
$('document').ready(function () {
var target;
$('#wrapper').mousedown(function (e) {
var el = $(e.target);
if (el.hasClass('elem')) {
target = el;
}
});
$('#wrapper').mouseup(function (e) {
var el = $(e.target);
//console.log(e);
if (target) {
if (el.hasClass('kletca')) {
el.append(target);
}
}
target = undefined;
});
});
}(jQuery));
http://jsfiddle.net/32DTv/ a simple example

Prevent lagging in DOM

So I am writing a sort of drawing script, and it works fine right now (although the code still needs to be cleaned up and there needs to be more features), but when painting too much, mousemove lags incredibly. Here is the main Javascript:
$('#canvas').on('mousedown', function(){
going = !going;
$(this).on('mousemove', function(e){
if(cursor == 'paint' && going == true){
$('.fall').each(function(){
if ($(this).css("opacity") == 0){
$(this).remove();
};
});
var ps = $('#canvas').offset().top;
var t = (e.pageY - ps - $('.fall').height()).toString() + 'px';
var l = (e.pageX - $('.fall').width()).toString() + 'px';
$('.fall').css("margin_left",l);
$('.fall').css("margin_top",t);
var doit = '<div class="fall" style="position:absolute;margin-left:' + l + ';margin-top:' + t + ';background-color:'+ color +';box-shadow: 0px 0px 5px ' + color + ';"></div>'
$('#canvas').prepend(doit);
}
else if(cursor == 'erase'){
$('.fall').mouseenter(function(){
$(this).fadeOut('fast',function(){
$(this).remove()
});
});
};
});
Essentially, when you click in the section for drawing, if the paint button is clicked, you can draw: jsfiddle.
My issue:
If you draw too much, especially with starting and stopping, it does not append enough on the mousemove do to (I assume) the DOM being overwhelmed.
Question:
What would be an efficient way to add many many divs to the DOM without creating a lag? Is this possible?
Note:
this is a personal project and I am not interested in using previously created drawing APIs
There's a lot you can do to improve performance.
The code below is a heavy refactoring of the code in the question. At first glance it might appear to be less efficient as it has about double the number of lines of the original. However, line count isn't the issue here. Two basic principles apply:
do as little DOM interaction as possible in the mousemove handler, and as much as possible on mousedown.
include a "divider circuit" to limit the number of times the mousemove handler is called. This is achieved by detaching the mousemove event handler on every call and reattaching after a short delay, conditional on the mouse still being down.
Also see comments in the code.
jQuery(function($) {
...
var $canvas = $("#canvas");
var data = {
name: 'fall'//a unique string for namespacing the muousemove event.
};
$canvas.on('mousedown', function() {
going = !going;
data.$fall = $('.fall');//this collection is created once per mousedown then managed inside mm to avoid unnecessary DOM interaction
data.mousedown = true;
data.colorCSS = {
'background-color': color,
'box-shadow': '0px 0px 5px ' + color
};
data.fallWidth = data.$fall.width();
data.fallHeight = data.$fall.height();
attachMouseMoveHandler();
}).on('mouseup', function() {
data.mousedown = false;
}).trigger('mouseup');
function attachMouseMoveHandler() {
if(data.mousedown);
$canvas.on('mousemove.' + data.name, mm);//the event is namespaced so its handler can be removed without affecting other canvas functionality
}
//The mousemove handler
function mm(e) {
if(going && cursor == 'paint') {
data.$fall.each(function() {
data.$fall = data.$fall.not(this);//manage data.$fall rather than re-form at every call of mm()
var $this = $(this);
if ($this.css("opacity") == 0) {
$this.remove();
};
});
data.$fall = data.$fall.add($('<div class="fall" />').css(data.colorCSS).prependTo($canvas)).css({
'margin-left': (e.pageX - data.fallWidth) + 'px',
'margin-top': (e.pageY - $canvas.offset().top - data.fallHeight) + 'px'
});
}
else if(cursor == 'erase') {
data.$fall.mouseenter(function() {
data.$fall = data.$fall.not(this);//manage data.$fall rather than re-form at every call of mm()
var $this = $(this).fadeOut('fast', function() {
$this.remove();
});
});
};
$canvas.off('mousemove.' + data.name);
setTimeout(attachMouseMoveHandler, 50);//adjust delay up/down to optimise performance
}
});
Tested only for syntax
I had to make a number of assumptions, chiefly concerning what becomes fixed data on mousedown. These assumptions may be incorrect, so you will most probably still have some work to do, but as long as you work inside the overall framework above, there's a good chance that your performance issues will disappear.

onmouseout and onmouseover

I am working on homework that involves working with javascript. Part of my homework assignment is to use the event handlers onmouseout and onmouseouver. What is supposed to happen when the user hovers over a specific div element, the font size grows by 25%, and when the user mouses out of the div element, the font size goes back to normal. My question is, is it possible to incorporate both an onmouseover function and an onmouseout function into one function? Somehow that is what my teacher wants us to do. I have this started so far.
function FontSize(x)
{
x.style.fonstSize = large;
}
I'm also thinking this isnt the correct code to make the font 25% larger, but I'm not sure how to really incorporate an onmouseout in this function.
As a teacher myself, I am 99% sure that by "one function" the instructor means one general-purpose function to change the font size, not one function which uses conditional statements to work backwards and figure out whether it should be doing onmouseout or onmouseover.
Your script should contain:
function resize(elem, percent) { elem.style.fontSize = percent; }
Your HTML should contain:
<div onmouseover="resize(this, '125%')" onmouseout="resize(this, '100%')"
Text within div..
</div>
Note: Situations such as here, are exactly why JavaScript has the keyword "this"--to save us from needing to use complicated document.getElementById() statements.
You can use "%" property for controlling font-size as described here with the following code.
document.getElementById("div1").onmouseover = function() {
document.getElementById("div1").style.fontSize = "125%"
};
document.getElementById("div1").onmouseout = function() {
document.getElementById("div1").style.fontSize = "100%";
};
Here is the working jsfiddle : http://jsfiddle.net/LxhdU/
Yes you can. Call the same function on both events, and pass a parameter to indicate whether the fontsize should increase or decrease.
ChangeFontSize = function(element, shouldIncreaseFontsize)
{
var small=14;
var large = small * 1.25;
if(shouldIncreaseFontsize) {
element.style.fontSize = large + "px";
}
else {
element.style.fontSize = small + "px";
}
}
http://jsfiddle.net/TMHbW/1/
I'd do something simple like the following. The large and small values can be whatever you need them to be for the font size to work or they can be variables you've defined in prior code.
Demo: http://jsfiddle.net/lucuma/EAbYn/
function doHover(e) {
if (e.type=='mouseover') {
this.style.fontSize = "large";
} else {
this.style.fontSize = "small";
}
}
var el = document.getElementById('myelement')
el.onmouseout =doHover;
el.onmouseover=doHover;
It is possible you do not need to call both the events on the element explicitly instead extension you create will do that.Extend the Element's prototype. Jquery also does similar to this.
Ref Prototype
See Fiddle:- http://jsfiddle.net/4fs7V/
Element.prototype.hover= function( fnOver, fnOut ) {
this.onmouseover=fnOver;
this.onmouseout=fnOut || fnOver;
return this;
};
document.getElementById('test').hover(function(){
//do your mouseover stuff
},
function(){
//do your mouseout stuff
});
Update
Same can be achieved with just one function too:-
Hover me
.largeFont {
font-size:125%;
}
Element.prototype.hover = function (fnOver, fnOut) {
this.onmouseover = fnOver;
this.onmouseout = fnOut || fnOver;
return this;
};
document.getElementById('test').hover(changeMe);
function changeMe()
{
if(this.hasAttribute('class'))
{
this.removeAttribute('class');
}
else
{
this.setAttribute('class', 'largeFont');
}
}

initialize mousemove event only after click

I am writing a plugin that creates a select marquee box when the mouse is clicked on a canvas div. So far i've written the plugin with the click() and mousemove() events adjacent to each other in the code (i dont know of any way to embed events inside other events). But this becomes a problem when the mouse moves over the canvas before a click. Does anyone know how to initialize a mousemove handler with a click?
Here is the code i have written so far:
$.fn.createBox = function(id) {
$(id).css({
cursor:'crosshair'
});
$(id).click(function(e) {
var clickLocX = e.pageX;
var clickLocY = e.pageY;
$('<div>').attr({
'class':'newBox',
'ctr':'on'
})
.css({
top:clickLocY,
left:clickLocX
})
.appendTo(id);
});
//Mousemove must be initialized only AFTER the click. HOW TO DO THIS?
$(id).mousemove(function(e){
var XpageCoord = e.pageX;
var YpageCoord = e.pageY;
window.Xloc = XpageCoord;
window.Yloc = YpageCoord;
var boxOffset = $('.newBox').offset();
var boxHeight = YpageCoord - boxOffset.top;
var boxWidth = XpageCoord - boxOffset.left;
$('.newBox').css({
height:boxHeight + 'px',
width:boxWidth + 'px'
});
});
}
Just set the mousemove handler from within the click handler and use a flag to determine whether or not it has already been set to avoid adding it multiple times.
EDIT: An example
assignedMoveHandler = false;
$(id).click(function(e) {
// other stuff...
if(!assignedMoveHandler) {
$(id).mousemove(function(e) {
// mouse move handling code...
});
assignedMoveHandler = true;
}
}
I use a global here which you may or may not care about, I lookup the jquery object twice, etc., so it could be cleaned up a bit, but this is the general idea. When the click handler fires you check to see if you've assigned a mousemove handler yet. If not, assign it.

copy, drag & drop bug : item stops moving

I'm trying to drag and drop items on a page, but insteads of moving the item itself, I create a copy of it.
Here is my code. "copyDragDrop" is a div at the bottom of the page. It remains empty until the user strats dragging something.
function coordSouris()
{
return {
x:event.clientX + document.body.scrollLeft - document.body.clientLeft,
y:event.clientY + document.body.scrollTop - document.body.clientTop
};
}
function drag()
{
var pos = coordSouris(event);
copie = event.srcElement.cloneNode(true);
document.getElementById('copieDragDrop').appendChild(copie);
copie.style.position = 'absolute';
copie.style.display = 'block';
document.onmousemove = mouseMove;
document.onmouseup = drop;
}
function mouseMove()
{
if (copie != null)
{
var pos = coordSouris(event);
copie.style.left = pos.x;
copie.style.top = pos.y;
}
}
function drop()
{
var divCopie = document.getElementById('copieDragDrop');
if (divCopie.hasChildNodes() )
{
while ( divCopie.childNodes.length >= 1 )
{
divCopie.removeChild(divCopie.firstChild);
}
}
}
This code creates the copy, starts to move it, but after a few pixels, the copy stops following the mouse. If I release the mouse, "onmouseup" is not fired, but the copy starts to follow the mouse again ! I've tried the code on several items, the same bug occurs
I don't understand anything, any help is more than welcome.
UPDATE : I juste realised that all elements I tried the code on had something in common : they contained or were included in an ASP.net hyperlink control. The same code works well on regular HTML elements. There must be some auto-generated javascript for links that interferes with my code.
Couldn't find the auto-generated code responsible for the issue, so I simply solvec this by replacing Hyperlink controls by standard HTML anchors.

Categories