Before I ask my question, here's the offending code:
var coords = dojo.coords(g);
g.style.left = coords.x + "px";
g.style.top = coords.y + "px";
g.style.position = "absolute";
Now imagine that g is a relatively positioned element with an x position of 70 and a y position of 30. If I comment out the last line of the above code, that's what dojo.coords(g) gives me. But as soon as I add the last line, dojo.coords(g) returns an x position of 18, which is where the element would be if it were absolutely positioned. It seems to me that the call to dojo.coords() shouldn't be affected by code below it, but it is.
I'm also seeing the same problem with dojo.require() where if I call it directly before the function call that it's loading, I get an undefined error for that given function.
I'm testing in Firefox 3, but I also am noticing similar problems in Safari 3. Any ideas?
It is not the Dojo quirk but the way browsers work. Basically it may take some time for DOM to reflect your changes and reposition elements, or even change the layout. That's why you see what you see.
Another reason may be in the parent of your element. dojo.coords() returns the position relative to the viewport. If you pass "true" as the second value, it will return the position relative to the document root. Assigning top/left directly and changing position to "absolute" makes the element positioned relatively to its parent, if it was positioned too (absolute, relative, or fixed), or to the document root. It is a subtle difference, but sometimes it bites people. Relevant references: Containing Block, MSDN: position Attribute, Google doctype: position.
PS: personally I rely on Dojo to set left/top hoping it'll do the right thing working around browser quirks, and I set the position first:
var coords = dojo.coords(g);
g.style.position = "absolute";
dojo.marginBox(g, {l: coords.x, t: coords.y});
I still would love someone to explain why this works, but here's a solution:
var coords = dojo.coords(g);
g.style.left = coords.x + "px";
g.style.top = coords.y + "px";
setTimeout(function(h) {
h.style.position = "absolute";
}, 0, g);
Cheers
sorry to not respond your question, (because I do not know dojo) but unless coords.x and coords.y returns a string, there is an error in your function,
g.style.left = coords.x + "px";
g.style.top = coords.y + "px";
You need to pass always a string to style, so add these also:
g.style.left = parseInt(coords.x) + "px";
g.style.top = parseInt(coords.y) + "px";
You would be surprised how many times this kind of error got my head spinning. About the answer, sorry, but I can't help you.
Good luck!
Edited:
Oh, if I understood, I can help you now.. well, since I do not know dojo, I can point out to you a javascript similar that will do what you want.
g.offsetTop
and
g.offsetLeft
Will give you the coords from the position of the element. Try them out and see if it works..
Related
Working on a little "zombies" or "tag you're it" or "ew! you got cooties"-styled game where each AI object (a person, basically) runs around randomly. There is an initial object that is "it" or "infected" and as it moves about the screen and touches/overlaps/collides with another object it should change the touched object to the same color as the object that touched it. Newly infected objects can continue to infect other objects they randomly collide with, until - in principle - the whole population is the same color as the first infected object. (I'll worry about fancier AI where infected actively hunt nearby objects or healthy objects can avoid infected objects, later).
But after looking at various similar questions in StackOverflow that generally deal with 2 DIVs colliding, or use some sort of jQuery draggable detection trick, I'm still at a bit of a loss as to how to build upon those ideas to scale up a simple "if I am touching/overlapping/colliding with another object it should get infected too" that can be applied to a large number of elements on the page, say... less than 100 so as not to drag the browser down.
I basically get as far as determining position and widths/heights of the objects so that I know how much space they take, but then the brain goes 'bzzzzt' when trying to develop a function that checks over all the population for collisions.
Got the population moving around randomly without trouble - see JSFiddle https://jsfiddle.net/digitalmouse/5tvyjhjL/1/ for the related code. Affected function should be in the 'animateDiv()', seen below to make the stackoverflow question asking editor happy that I included some code in my question. :)
function animateDiv($target) {
var newq = makeNewPosition($target.parent());
var oldq = $target.offset();
var speed = calcSpeed([oldq.top, oldq.left], newq);
// I believe collision should be dealt with here,
// just before moving an object
$target.animate({
top: newq[0],
left: newq[1]
}, speed, function () {
animateDiv($target);
});
}
Any hints, tricks, adaptations, or code snippets that push me in the right direction are appreciated.
a quick, down and dirty solution (there are more complex algorithms) would be to use:
document.elementFromPoint(x, y);
It gets the element at the position specified. The full spec can be found here.
Assuming your 'zombies' are rectangular, you could call this for each corner, and if you get a hit, that isn't the background or the element you're checking, you've got a collision...
EDIT:
An alternate method, even 'downer and dirtier' than above, but stupidly quick, would be to get the centre points of the two objects to check, then find their absolute displacements in X and Y. If the differences are less than the sum of half their widths and heights then they are overlapping. It's by no means pix perfect, but it should be able to handle a large number objects really quickly.
EDIT 2:
First off, we need to get the centres of each object (to check)
// Values for main object
// pop these in vars as we'll need them again in a sec...
hw = object.style.width >> 1; // half width of object
hh = object.style.height >> 1; // (bit shift is faster than / 2)
cx = object.style.left + hw; // centre point in x
cy = object.style.top + hh; // and in y
// repeat for secondary object
If you don't know / store the width and height you can use:
object.getBoundingClientRect();
which returns a 'rect' object with the fields left, top, right and bottom.
Now we check proximity...
xDif = Math.abs(cx - cx1); // where cx1 is centre of object to check against
if(xDif > hw + hw1) return false; // there is no possibility of a collision!
// if we get here, there's a possible collision, so...
yDif = Math.abs(cy - cy1);
if(yDif > hh + hh1) return false; // no collision - bug out.
else {
// handle collision here...
}
Danny
I have the following block of code that I need to complete as quickly as possible:
//someSpans is an array of spans, with each span containing two child spans inside of it
$.each( someSpans, function (i, span) {
//Get the span widths, then add them to the style to make them permanent
var aSpan = span.children[0];
var bSpan = span.children[1];
span.style.width = (aSpan.offsetWidth + bSpan.offsetWidth) + 'px';
aSpan.style.width = aSpan.offsetWidth + 'px';
bSpan.style.width = bSpan.offsetWidth + 'px';
});
If someSpans is an array that contains 1000 objects, this loop presented above will cause 3000 browser redraws, even though nothing on screen is actually changing, since the new "width" attributes in the style match the existing "auto" width. Is there a way to prevent the browser from redrawing the CSS until the loop is finished? I feel like this will greatly reduce the time it takes for the loop to complete.
I feel like requestAnimationFrame might be the key to doing what I'm looking for, but maybe I'm off base.
While the comments of why make a great point, here's a little better answer.
Part of your problem here is the alternating reads/writes from the style. Namely, setting span.style.width has now made aSpan.offsetWidth "dirty" and the CSS must be rendered. However, consider this:
var aWidth = aSpan.offsetWidth;
var bWidth = bSpan.offsetWidth;
span.style.width = (aWidth + bWidth) + 'px';
aSpan.style.width = aWidth + 'px';
bSpan.style.width = bWidth + 'px';
The rendering is now cut down to once per loop. More specifically, it's in reading offsetWidth on the next iteration that causes the render.
Exercise: While it can make code a little more obtuse, sometimes unnecessarily so, I have sometimes written code like this to loop twice. The first time collects the operations into an array, and the second loop is able to combine all the "setting" operations without accessing any layout values.
MSDN has some great documents on JavaScript performance with the most applicable here being "Managing layout efficiently"
Hello fellow code people :)
I am a frontend web developer and as such in need of constant knowledge of the actual viewport size in order to see where in responsive designing breakpoints start and end.
I know FF's own 'test window size' function, but came across a very handy extension: FireSizer.
the extension has one itsy bitsy drawback: It gives back the window-size including FF's borders and scrollbar. I need the viewport-size though. So I need the extension hacked, but dont't know enough javaScript to do so. Maybe someone is willing to help em out here?
I would love the extension to actually look for the scrollbar, and subtract from the width
a) 14 if no scrollbar present or
b) 30 if scrollbar present
I found of what I think is the right place to alter the code:
//
// Update the status bar panel with the current window size
//
function FiresizerUpdateStatus() {
var width = window.outerWidth + ''; // <- Think code needs to be edited here
var height = window.outerHeight + '';
document.getElementById("firesizer-statuspanel").label = width + 'x' + height;
}
Thanks for any effort!
AO
#Chen Asraf:
Well thank you very much. I didn't know there was an element to call the document-width. I changed the code to the following, and that did the trick (also when compared to FF's own 'Responsive Design View mode', which is spot on, its off by 2px - which i subtract from clientWidth.)
function FiresizerUpdateStatus() {
var width = window.outerWidth + ''; // changed this line to:
var width = document.documentElement.clientWidth-2 + '';
var height = window.outerHeight + '';
document.getElementById("firesizer-statuspanel").label = width + 'M' + height;
}
Thanks
AO
Possible duplicate of Get the browser viewport dimensions with JavaScript
Seems like you can get the window's inner dimensions by using:
// My window is maximized; screen is 1366x768
alert(document.documentElement.clientWidth);
// ^ returns 1349 (17 missing pixels because of scrollbar)
alert(document.documentElement.clientHeight);
// ^ returns 643 (125 pixels missing because of start bar & Chrome toolbars)
You can then compare the following with whatever else you need (for example, compare client width with window width to find if the difference is big enough to be a scrollbar - just experiment with the sizes)
I'm try to get the browser viewport size.
When the page initially loads (in jQuery(function() { .. });) , both these show the correct value (eg: 560):
console.log($(window).height());
console.log(document.documentElement.clientHeight);
But later when I do the same thing, it shows the height of the whole docoument (eg: 11675).
There's a lot of HTML and JS and it would take a while to figure out what's going on, I was just wondering, did anyone see anything like this, if so, what can cause it and how can I get the correct size of the viewport? All google hits show that's the correct way to retrieve the value.
Note: I'm using chrome.
I recently bumped into the same problem in one of my projects. I didn't have time to dig and isolate this weird bug, and I ended up using this function (adapted from this answer) to correctly get the viewport dimensions :
var getViewportSize = (function(){
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0];
return function(){
return {
w : Math.max(w.innerWidth || e.clientWidth || g.clientWidth, app.config.minWidth),
h : Math.max(w.innerHeight|| e.clientHeight|| g.clientHeight, app.config.minHeight)
};
}
})();
From what I've tested, jQuery returned the incorrect size when the console or some other browser extension/toolbar was occupying some of the viewport space.
Hope this helps, but I'm also curious and trying to figure this one out, because it's hard to think that a mature lib such as jQuery 2.0 has these kind of bugs.
I am wanting to create a simple abstract pattern using the html5 canvas tag and javascript. I have worked out the guts of what I want it to do using some variables, functions and objects, but with the boundary detection that I have employed I am wanting each particular shape to go back to its starting position when it goes out of the screen (and thus loop the animation).
So with that being my question, here is my code. Also any other structure tips are appreciated as I am new to OO in Javascript.
See my progress here: http://helloauan.com/apps/test/
Cheers!
I'm not really sure if what you mean exactly is, once the big white diagnal lines are all the way off the top right corner of page, that's when you want them to start back at the bottom left ? right?
What you need to do is check if the line is beyond the width and height of the canvas, and in your case, the window itself since the canvas fills the browser window. So you need to do a series of conditionals. You check if the line x + line width is > canvas width and line.y + line height is > canvas height. If both are true then set the x and y of the line to - what it is at that time. So something like:
if( line.x + line.width > canvas.width && line.y + line.height < 0) {
line.x = -0;
line.y = canvasHeight + line.height;
}
This is how I recycle circles that come in from the right side of the screen and once they exit the left side they start over on the right.
if( d.x + d.radius < 0 ) {
d.radius = 5+(Math.random()*10);
d.x = cwidth + d.radius;
d.y = Math.floor(Math.random()*cheight);
d.vX = -5-(Math.random()*5);
}
The first thing is just psuedo, you should take a look at a thing I made to use as a starting point for things like this. The structure of your code could use some more organization, canvas gets real complex real quick.
Using the arrow keys, move the square off any one of the 4 sides and see it come in on opposite side.
http://anti-code.com/games/envy/envy.html
Fork if you want: https://github.com/jaredwilli/envy