I have build a grid of div's as playground for some visual experiments. In order to use that grid, i need to know the x and y coordinates of each div. That's why i want to create a table with the X and Y position of each div.
X:0 & Y:0 = div:eq(0), X:0 Y:1 = div:eq(1), X:0 Y:2 = div:eq(2), X:0 Y:3 = div:eq(3), X:1 Y:0 = div:eq(4) etc..
What is the best way to do a table like that? Creating a OBJECT like this:
{
00: 0,
01: 1,
02: 2,
etc..
}
or is it better to create a array?
position[0][0] = 0
the thing is i need to use the table in multiple way's.. for example the user clicked the div nb: 13 what are the coordinates of this div or what is the eq of the div x: 12 y: 5.
Thats how i do it right now:
var row = 0
var col = 0
var eq = 0
c.find('div').each(function(i){ // c = $('div#stage')
if (i !=0 && $(this).offset().top != $(this).prev().offset().top){
row++
col = 0
}
$(this).attr({'row': row, 'col': col })
col++
})
I think it would be faster to build a table with the coordinates, instead of adding them as attr or data to the DOM. but i cant figure out how to do this technically.
How would you solve this problem width JS / jQuery?
A few questions:
Will the grid stay the same size or will it grow / shrink?
Will the divs stay in the same position or will they move around?
Will the divs be reused or will they be dynamically added / removed?
If everything is static (fixed grid size, fixed div positions, no dynamic divs), I suggest building two indices to map divs to coordinates and coordinates to divs, something like (give each div an id according to its position, e.g. "x0y0", "x0y1"):
var gridwidth = 20, gridheight = 10,
cells = [], // coordinates -> div
pos = {}, // div -> coordinates
id, i, j; // temp variables
for (i = 0; i < gridwidth; i++) {
cells[i] = [];
for (j = 0; j < gridheight; j++) {
id = 'x' + i + 'y' + j;
cells[i][j] = $('#' + id);
pos[id] = { x: i, y: j };
}
}
Given a set of coordinates (x, y) you can get the corresponding div with:
cells[x][y] // jQuery object of the div at (x, y)
and given a div you can get its coordinates with:
pos[div.attr('id')] // an object with x and y properties
Unless you have very stringent performance requirements, simply using the "row" and "col" attributes will work just fine (although setting them through .data() will be faster). To find the div with the right row/col, just do a c.find("div[row=5][col=12]"). You don't really need the lookup.
Let me elaborate on that a little bit.
If you were to build a lookup table that would allow you to get the row/col for a given div node, you would have to specify that node somehow. Using direct node references is a very bad practice that usually leads to memory leaks, so you'd have to use a node Id or some attribute as a key. That is basically what jQuery.data() does - it uses a custom attribute on the DOM node as a key into its internal lookup table. No sense in copying that code really. If you go the jQuery.data() route, you can use one of the plugins that allows you to use that data as part of the selector query. One example I found is http://plugins.jquery.com/project/dataSelector.
Now that I know what it's for...
It might not seem efficient at first, but I think It would be the best to do something like this:
Generate the divs once (server side), give them ids like this: id="X_Y" (X and Y are obviously numbers), give them positions with CSS and never ever move them. (changing position takes a lot of time compared to eg. background change, and You would have to remake the array I describe below)
on dom ready just create a 2D array and store jquery objests pointing the divs there so that
gridfields[0][12] is a jQuery object like $('#0_12'). You make the array once and never use selectors any more, so it's fast. Moreover - select all those divs in a container and do .each() on them and put them to proper array fields splitting their id attributes.
To move elements You just swap their css attributes (or classes if You can - it's faster) or simply set them if You have data that has the information.
Another superfast thing (had that put to practice in my project some time ago) is that You just bind click event to the main container and check coordinates by spliting $(e.target).attr('id')
If You bind click to a grid 100x100 - a browser will probably die. Been there, did that ;)
It may not be intuitive (not changing the div's position, but swapping contents etc.), but from my experience it's the fastest it can get. (most stuff is done on dom ready)
Hope You use it ;) Good luck.
I'm not 100% sure that I understand what you want, but I'd suggest to avoid using a library such as jQuery if you are concerned about performance. While jQuery has become faster recently, it still does has more overhead than "pure" JS/DOM operations.
Secondly - depending on which browsers you want to support - it may even be better to consider using a canvas or SVG scripting.
Related
I am implementing a set of custom elements that will be used like this:
<om-root>
...
<om-node id="node1">
...
<om-node id="node2">
...
</om-node>
...
</om-node>
...
<om-root>
That is, my <om-node> elements will be mixed in with arbitrary HTML, which may have positioning and/or CSS transform applied.
The purpose of these <om-node> elements is to apply CSS affine transformations to their content based on various conditions. But regardless of its position in the hierarchy, each om-node computes a transformation relative to the root node.
I can't just apply the computed transformation to the node, because the browser will combine that with the transformations of all its ancestor elements: if I rotate node1 by 30 degrees, then node2 will also be rotated by 30 degrees before its own transformation is applied.
Ideally, what I want is something that works like Element.getClientRects(), but returns a matrix rather than just a bounding box. Then I could do some math to compensate for the difference between the coordinate systems of the <om-node> and <om-root> elements.
This question is similar to mine, but doesn't have a useful answer. The question mentions using getComputedStyle(), but that doesn't do what is claimed – getComputedStyle(elt).transform returns a transformation relative to the element's containing block, not the viewport. Plus, the result doesn't include the effects of "traditional" CSS positioning (in fact it doesn't have a value at all for traditionally-positioned elements).
So:
Is there a robust way to get the transformation matrix for an element relative to the viewport?
The layout engine obviously has this info, and I'd prefer not to do a complicated (and expensive) tree-walking process every time anything changes.
Having thought some more about the question, it occurred to me that, in fact, you can solve the problem using getBoundingClientRect().
Of course, getBoundingClientRect() on its own does not tell you how an element has been transformed, because the same bounding box describes many possible transformations:
However, if we add three child elements, with a known size and position relative to the parent, then we can figure out more information by comparing each of their bounding boxes. The figure below shows where I have placed these three "gauge" elements (which in practice are empty and invisible):
The vectors u̅ and v̅ are orthogonal unit vectors of the parent element's untransformed coordinate system. After the element has been transformed by various CSS positioning and transform properties, we first need to find the transformed unit vectors u̅' and v̅'. We can do that by comparing the bounding boxes of the three gauge elements – the diagram below shows the process with two different example transformations:
the vector from box 1 to box 2 is equivalent to u̅'
the vector from box 1 to box 3 is equivalent to v̅'
the midpoint between [the top left of box 3] and [the bottom right of box 2] gives us point P: this is the transformed position of the parent element's origin
From these three values u̅', v̅' and P we can directly construct a 2D affine transformation matrix T:
This matrix T represents all the transformations affecting the parent element – not just CSS transform rules, but also "traditional" positioning, the effects of margins and borders, etc. And, because it's calculated from getBoundingClientRect(), it is always relative to the viewport – you can compare any two elements directly, regardless of their relationship within the DOM hierarchy.
Note: all this assumes we are only dealing with 2D affine transformations, such as transform:rotate(30deg) or left:120px. Dealing with 3D CSS transforms would be more complicated, and is left as an exercise for the reader.
Putting the above into code form:
class WonderDiv extends HTMLElement {
constructor () {
super();
this.gauges = [null, null, null];
}
connectedCallback () {
this.style.display = "block";
this.style.position = "absolute";
}
createGaugeElement (i) {
let g = document.createElement("div");
// applying the critical properties via a style
// attribute makes them harder to override by accident
g.style = "display:block; position:absolute;"
+ "margin:0px; width:100px; height:100px;"
+ "left:" + ( ((i+1)%2) ? "-100px;" : "0px;")
+ "top:" + ( (i<2) ? "-100px;" : "0px;");
this.appendChild(g);
this.gauges[i] = g;
return g;
}
getClientTransform () {
let r = [];
let i;
for (i=0; i<3; i++) {
// this try/catch block creates the gauge elements
// dynamically if they are missing, so (1) they aren't
// created where they aren't needed, and (2) they are
// restored automatically if someone else removes them.
try { r[i] = this.gauges[i].getBoundingClientRect(); }
catch { r[i] = this.createGaugeElement(i).getBoundingClientRect(); }
}
// note the factor of 100 here - we've used 100px divs
// instead of 1px divs, on a hunch that might be safer
return DOMMatrixReadOnly.fromFloat64Array(new Float64Array([
(r[1].left - r[0].left) / 100,
(r[1].top - r[0].top) / 100,
(r[2].left - r[0].left) / 100,
(r[2].top - r[0].top) / 100,
(r[1].right + r[2].left) /2,
(r[1].top + r[2].bottom) /2
]));
}
}
customElements.define("wonder-div", WonderDiv);
– the custom <wonder-div> element extends <div> to have a getClientTransform() method, which works like getClientBoundingRect() except that it returns a DOMMatrix instead of a DOMRect.
CSS Transformations are actually relatively heavy operations and do come with some gotchas.. (they TRANSFORM elements) so you may not be able to avoid traversing the nodes without implementing an intelligent state system, for example, storing all your objects + transformation in your javascript class..
That said, one easy workaround for small use cases is to disable transform on all the parent elements using something like 'inline' but this is not suitable for all cases..
<div id="outside">
<div id="inside">Absolute</div>
</div>
document.getElementById('outside').style.display = "inline";
The more robust approach is to retrieve and parse the computedStyles dynamically ...
function getTranslateXY(element) {
const style = window.getComputedStyle(element)
const matrix = new DOMMatrixReadOnly(style.transform)
return {
translateX: matrix.m41,
translateY: matrix.m42
}
}
Then you can dynamically set new transformations on any node by adding/subtracting from the current transformation state.
I have been working with the following code: http://bl.ocks.org/NPashaP/7683252. This is a graphical representation of a tree. I have stripped away much of the code (the graceful labels), only allowing two nodes per parent, and changed the data structure to an array.
The only problem left now is the repositioning. The original code does that perfectly. But since I want a binary tree, I have let the user choose to insert a left or right child. The original reposition code animates the first child straight down from the parent, but that would be wrong in a binary tree. I want it to either go to the left or right.
reposition = function (v) {
function repos(v) {
var lC = getLeafCount(v.v),
left = v.p.x; //parent's x-position
v.c.forEach(function (d) {
var vc = d; //saving reference of the child in parent object
d = tree.getVerticeById(d.v); //actually fetching the child object
var w = 0;
if(d.d == 'right') { w += 15 * lC }
if(d.d == 'left') { w -= 15 * lC }
d.p = {x: left + w, y: v.p.y + tree.h}; //setting the position
vc.p = d.p; //setting the child's pos in parent obj
repos(d);
});
}
repos(v[0]);
};
Some of the parts of my code are different from the original code, because I have changed the data structure as stated before. I have tried to comment the parts that may be confusing, but what is important is the math of the repositioning.
At first, this code seemed to work well (https://i.stack.imgur.com/gjzOq.png). But after some testing I discovered a huge problem with the repositioning: the nodes crash with each other (https://i.stack.imgur.com/pdQfy.png)!
Conclusion: I have tried to modify the original function to take the in mind the left and right positioning of nodes, but could not do it. I wrote this variant of the method which does, but it still has some problems as illustrated in the pictures. I would be thankful for some input in this matter.
I eventually did a quick fix for this problem.
After running the reposition function as posted in the question, i ran another function which fixed the problem. The new function goes through all levels of the tree and checks if the nodes are too close together. If they are, the algorithm finds closest common ancestor, and increases the distance between its left and right children. After this, there is no more collisions between nodes.
In order to determine the page margin in various positions on the page I need to get the offset, width and height of each DOM element.
I loop over the DOM elements recursively and save each element's attributes.
I tried it with JQuery $(el).offset() but it was very slow. I guess that creating a JQuery object for each element by using the$(...) is very slow.
Then, I tried an old implementation that I got from an old code which uses native JS and it was X4 times faster.
SO what I'm really asking, is there a different method to accomplish that?
I should mention that my script runs on publishers' sites and I don't really know anything about the page that I'm running on.
I guess this is one solution, using map() and getBoundingClientRect():
var elements = document.getElementsByClassName('someclass');
elements.map(function(val, i, arr) {
var doc = document.getBoundingClientRect(),
el = val.getBoundingClientRect(),
coordsX = el.left - doc.left,
coordsY = el.top - doc.top;
return {x: coordsX, y: coordsY};
});
This will return an array of all the coordinates of a given class. You can change document.getElementsByClassName to querySelector if it better suits your needs.
This will also change getBoundingClientRect() to be relative to the document instead of the viewport.
Im Recently new here, we have made for our school project a cell detection. That functions under the microscope and detect the cells from PNG images that come out of our software.
On these images there will be a color detection done from tracking.js and then it returns te X and Y position of the image dimensions of the center of the cell.
Our concerning is that the cells we have under our microscope evolve so we take every minute automatically an image of this. So we have a set of 10 images with cells and also dissapearing and new cells. What would be the best way to identify them as new cells, and the cells still exist in the timeframe to identify them by an ID, and the dissapearing cells remove as object.
A lot of text, lets see what we've got right now.
Now as the detection works fine thats great. For each cell we make a new object with the following code:
var cells = [];
celltrack = function (x, y) {
xoutput = (img.offsetLeft + x);
youtput = (img.offsetTop + y);
cells = new cell('cell');
cells.id = count++;
cells.x = xoutput;
cells.y = youtput;
}
cell = function (type) {
this.type = type;
this.id = 0;
this.x = "x";
this.y = "y";
}
A couple of main questions on this moment:
Is this the correct way to create the objects containing the cell info ?
What is the best way to identify a cell before create of delete a object ?
If a cell has moved, how can we link the cell to the same object with 2 different locations.
Thats it for now, if we have got further improvements in our project we keep you informed.
I assume the problem is:
Every minute you will get an image of cells, then properties of cells will be analyzed, the output is a series (an array) of positions for the cells in the image
You would like to track how cells create/disappear/move between time periods
With those assumptions, I think:
Yes, you can use the cell constructor function defined in your snippet, though normally we will capitalize constructor functions (to indicate they should be used with new keyword, as in Cell). The properties that goes into the newly created object are up to you, now they are type, id, x and y.
I think that depends on the rules of identifying cells, based on the input we have (just positions), I am not sure we have a good solution here. Are there other properties of cells we can consider and extract in each image?
It relates to the last question, but once you can identify cells, you can update the cell objects between time periods, the history of locations for a cell can be kept in the Cell object itself, maybe a property called locations of type Array.
I’m generating multiple, random sized, circular elements using the Raphael JavaScript library but because it’s random a lot of the circular elements being generate overlap or cover each other. What I wanted to know, is there any way with JavaScript to tell if one element is in already in particular position so to avoid the overlapping? Essentially, I want to create random elements on a canvas, of a random size that don’t overlap or cover each other.
There's a couple of test files I created here to give you an idea of what I'm doing. The first one generates random objects and the second link sets them to a grid to stop the overlapping.
http://files.nicklowman.co.uk/movies/raphael_test_01/
http://files.nicklowman.co.uk/movies/raphael_test_03/
The easiest way is to create an object and give it a repulsive force that degrades towards zero at it's edge. As you drop these objects onto the canvas the objects will push away from each other until they reach a point of equilibrium.
Your examples aren't working for me, so I cannot visualize your exact scenario.
Before you "drop" an element on the canvas, you could query the positions of your other elements and do some calculations to check if the new element will overlap.
A very simple example of this concept using circle elements might look like this:
function overlap(circ1, circ2) {
var attrs = ["cx", "cy", "r"];
var c1 = circ1.attr(attrs);
var c2 = circ2.attr(attrs);
var dist = Math.sqrt(Math.pow(c1.cx - c2.cx ,2) + Math.pow(c1.cy - c2.cy, 2));
return (dist < (c1.r + c2.r));
}
var next_drop = paper.circle(x, y, r);
for (var i in circles) {
if (overlap(next_drop, circles[i])) {
// do something
}
}
Of course calculating just where you're going to place a circle after you've determined it overlaps with others is a little more complicated.