There are a few similar questions on Stack but I think mine is the result of some syntax error on my part. Anyway, I'm building a lightbox in javascript and want to dynamically set the absolute position (left % css) to be half of the width of the element.
This is a snippet from the code. Full code is here http://jsfiddle.net/Yab3Q/4/
var modal = document.getElementById(btnId+"-modal");
var modalChildren = modal.childNodes;
for (var x = 0; x<modalChildren.length; x++){
if (!(modalChildren[x].className === "lightBox")){
var childWidth = modalChildren[x].offsetWidth;
var halfWidth = childWidth / 2;
modalChildren[x].style.left = halfWidth + "px";
}
}
The line modalChildren[x].css.left = halfWidth + "px"; is returning "Uncaught TypeError: Cannot set property 'left' of undefined". However, modalChildren[x].className and modalChildren[x].offsetWidth; both return the expected values, so I'm unsure why I can view but not update the css here.
You try to alter the style properties of the following elements:
0: <div>
1: #text
2: <img>
3: #text
You can see this list for yourself by typing
console.log(modalChildren)
since a textNode does not have a style property, an error is thrown
text node has no style property:
log and check:
for (var x = 0; x<modalChildren.length; x++){
console.log(modalChildren[x]);
console.log(modalChildren[x].style);
if (!(modalChildren[x].className === "lightBox")){
var childWidth = modalChildren[x].offsetWidth;
var halfWidth = childWidth / 2;
modalChildren[x].style.left = halfWidth + "px";
}
}
Take a look at node types: http://www.javascriptkit.com/domref/nodetype.shtml
Related
I am trying to create a word cloud. In order to render text to the screen I am generating a random position for each word. This works perfectly, however there are a lot of overlapping words. In order to solve this I am storing the position and size of the elements in an array and then I created a helper function that checks for collisions, generates a new position for the element if it finds one, and then calls it's self again to check again from the start of the array. When I run my code the first 2-3 words render just fine but then I get an error saying Maximum call stack size exceeded. I saw there was already a post on this same issue on stack overflow.
I saw that the other person was using a forEach function and so was I so I converted it into a for loop like the answer suggested but it did not do anything. I think the issue boils down to the fact that there are so many collisions but I am not sure how to best approach the issue. Is there another way that I can generate unique positions for elements while still avoiding collisions?
Code:
function calculatePosition(parent, child) {
return Math.random() * parent - (child / 2)
}
// needed for rendering position of span elements
var ranges = []
var totalWidthOfWords = 0
var totalHeightOfWords = 0
// reposition element if there is a collision
function checkForCollisions(element, height, width, wordCloud, injectedSpan) {
for(var i = 0; i < ranges.length; i++) {
let current = ranges[i]
if(element.left >= current.width[0] && element.left <= current.width[1]) {
injectedSpan.style.left = calculatePosition(wordCloud.clientWidth, width) + "px";
checkForCollisions(element, height, width, wordCloud, injectedSpan)
}
if(element.top >= current.height[0] && element.top <= current.height[1]) {
injectedSpan.style.top = calculatePosition(wordCloud.clientHeight, height) + "px";
checkForCollisions(element, height, width, wordCloud, injectedSpan)
}
}
}
// Create content in DOM
const injectedContent = data.map(line => {
const injectedSpan = document.createElement("span")
const injectedWord = document.createElement("p")
const wordCloud = document.querySelector(".word-cloud")
// mod weight value to get more managable inputs
let weightValue = (line.weight * 100).toFixed(2)
// sets values of words and renders them to the screen
injectedWord.innerText = line.word
injectedSpan.appendChild(injectedWord)
wordCloud.appendChild(injectedSpan)
// sets style attribute based on weight value
injectedWord.setAttribute("style", `--i: ${weightValue}`)
// flips words
if(Math.random() > 0.75) {
injectedWord.style.writingMode = "vertical-rl";
}
// Entrance animation
let left = innerWidth * Math.random()
let top = innerHeight * Math.random()
if(Math.random() < 0.5) {
injectedWord.style.left = "-" + left + "px";
injectedSpan.style.left = calculatePosition(wordCloud.clientWidth, injectedSpan.clientWidth) + "px";
} else {
injectedWord.style.left = left + "px";
injectedSpan.style.left = calculatePosition(wordCloud.clientWidth, injectedSpan.clientWidth) + "px";
}
if(Math.random() < 0.5) {
injectedWord.style.top = "-" + top + "px";
injectedSpan.style.top = calculatePosition(wordCloud.clientHeight, injectedSpan.clientHeight) + "px";
} else {
injectedWord.style.top = top + "px";
injectedSpan.style.top = calculatePosition(wordCloud.clientWidth, injectedSpan.clientWidth) + "px";
}
// Get position of span and change coordinites if there is a collision
let spanPosition = injectedSpan.getBoundingClientRect()
console.log(spanPosition)
if(spanPosition) {
checkForCollisions(spanPosition, spanPosition.height, spanPosition.width, wordCloud, injectedSpan)
}
totalWidthOfWords += spanPosition.width
totalHeightOfWords += spanPosition.height
ranges.push({width: [spanPosition.left, spanPosition.right], height: [spanPosition.top, spanPosition.bottom]})
})
Link: https://jsfiddle.net/amotor/mdg7rzL1/4/
There is still a lot of work to do to make sure that it works properly, especially to make sure that the code does not produce any errors!
The general idea would be to follow IllsuiveBrian's comment to make sure, that checkForCollision only does the work of checking if there is a collision and that another function takes care of recalculating the position if necessary and then reevaluating a potential collision.
function checkForCollisions(element, wordCloud, injectedSpan) {
for(var i = 0; i < ranges.length; i++) {
let current = ranges[i];
// return true if there is a collision (you probably have to update the code you are using here to truly avoid collisions!)
if (collision) { return true; }
}
return false; // return false otherwise
}
Finally this part would take care of recalculating position and and rechecking for collision:
ranges.forEach(function(injectedSpan) {
// Get position of span and change coordinites if there is a collision
let spanPosition = injectedSpan.getBoundingClientRect();
if (spanPosition) {
while (checkForCollisions(spanPosition, wordCloud, injectedSpan)) {
injectedSpan.style.left = calculatePosition(wordCloud.clientWidth, element.width) + "px";
injectedSpan.style.top = calculatePosition(wordCloud.clientHeight, element.height) + "px";
}
}
});
Here is a quick idea on how to go into this direction: https://jsfiddle.net/euvbax1r/4/
I have a list with 4 numbers. If I divide 100 with the list's length, I get 25. I want to set width of four elements to multiples of this number, eg. for the first, it would be 25px, for the second 50px and so on.
This is the (pseudo)code I've written so far:
list{1,2,3,4}
var array = list.split(',');
var width = 100 / array.length;
for (var n = 0; n < array.length; n++) {
if(array[n]==1) {
width = width; //here I want width as 25;
<div style="width:"+Width +"></div>
}
if(array[n]==2){
width = width+width;//here I want width as 50
<div style="width:"+Width +"></div>
}
if(array[n]==3) ){
width = width+width;//here I want width as 75
<div style="width:"+Width +"></div>
}
if(array[n]==4 ){
width = width+width;//here I want width as 100
<div style="width:"+Width +"></div>
}
}
It seems like you mix up html and javascript syntax. Html are those <tag> things.
First of all, I recommend you this course or some other, just to get started with JavaScript.
To create an element in JS, you can either use document.write, which is probably much easier but may be used only before the document loads. You can use it like this:
width = 42; //or whatever
document.write('<div style="width: '+width+'px">adsf</div>');
Or the more difficult, but also more flexible way – to use the DOM. You would do it this way:
var div = document.createElement('div'); //create new element
div.style.width = 42; //set its width to whatever you want
div.textContent = "some text"; //add some text into the div
someElement.appendChild(div); //insert the element into another one
The someElement here is either an element you get by calling document.getElementById (or a similar function) or, if you want them directly inside the body, just write document.body.
.
With respect to #Marc_B's answer, the final code would look something like this:
var list = [1,2,3,4];
var div;
for (var n = 0; n < array.length; n++) {
div = document.createElement('div');
div.style.width = 25*n;
document.body.appendChild(div);
}
You do NOT need all those if() tests:
for (var n = 0; n < array.length; n++) {
width = 25 * (n + 1);
...
}
s0
n = 0 -> width = 25 * (0 + 1) -> 25 * 1 -> 25
n = 1 -> width = 25 * (1 + 1) -> 25 * 2 -> 50
n = 2 -> width = 25 * (2 + 1) -> 25 * 3 -> 75
etc...
Not 100% sure what your asking but I think you just want to times width by 2,3,4 depending on what n is.
(Go to Marc B's answer)
Edit: BTW when you declare width you by mistake (I think) use a capital W
var Width
Should be:
var width
I'm writing a mini-game in EaselJS that requires mouse clicks on a grid of tiles.
However, it appears that it's only partially working - or perhaps not assigning a unique Event to each bitmap. During creation, I assign a simple ID - a number counting up per iteration, and expect to get that back.... but no matter where I click, out of 49 objects on stage (hexagon tiles), it only reports back 48....every time. I have enabled stage.enableMouseOver(20), which I initially forgot, but it did not help.
function BuildGround()
{
var i = 0;
for (var x = 0; x < 7; x++)
{
for (var y = 0; y < 7; y++)
{
var h = 102;
var s = h/Math.cos(30*Math.PI/180)/2;
var tempTile = new createjs.Sprite(Tiles, 0);
WorldContainer.addChild(tempTile);
tempTile.regX = 64;
tempTile.regY = 64;
tempTile.id = i;
tempTile.x = x * s * 1.5;
tempTile.y = y * h + (x % 2) * h / 2;
tempTile.addEventListener("click", function(event) { alert(tempTile.id); })
TileArray.push(tempTile);
i++;
console.log(i);
}
}
WorldContainer.x = 155;
WorldContainer.y = 150;
}
My guess, you are experiencing context problem of JavaScript, try this:
tempTile.addEventListener("click", function(event) { alert(event.target.id); })
All events objects have a target element, usually the one which triggered it.
I've just made two simple functions which I linked to the onClick property of two buttons on a HTML page.
One function looks up the width of a div and then it should just add '1', but instead it adds '3'. The other function looks up the width of a div and then it should subtract '1'. Instead of doing that, it adds '1'
Could someone enlighten me why this happens and show me to correct this?
function bigger() {
var div_kubus = document.getElementById('kubus');
var x = div_kubus.offsetWidth;
var y = parseInt(x) + 1;
var tekst = document.getElementById('tekst');
tekst.value = y;
div_kubus.style.width = y + 'px';
/*div_kubus.setAttribute('style','width:'+y+'px');*/
}
function smaller() {
var div_kubus = document.getElementById('kubus');
var x = div_kubus.offsetWidth;
var tekst = document.getElementById('tekst');
x -= 1;
div_kubus.setAttribute("style", "width:" + x + "px");
tekst.value = x;
}
.offsetWidth will return the width including borders and padding:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.offsetWidth
So I'm assuming that you've probably got a 1px border (or padding) that is adding 2 to your expected width (1px for the left, 1px for the right). Therefore, adding 1, adds 3; and subtracting 1, adds 1.
I'm trying to get the height of a div, then position it based on the height. I've read various articles on this, but I always seem to be getting NaN as a result. Yes the javascript is loaded at the end of the body, after the divs are drawn.
I'm using asp.net to create this javascript dynamically. What I have output so far is:
var resultsDiv = document.getElementById('resultsDiv');
var resultsInnerDiv = document.getElementById('resultsInnerDiv');
resultsInnerDiv.innerHTML = "test";
var h = parseInt(resultsInnerDiv.style.offsetHeight);
alert(parseInt(resultsInnerDiv.style.offsetHeight));
resultsInnerDiv.style.top = ((h / 2) -125) + 'px';
I need to get the actual height, instead of NaN. NaN pixels is obviously not valid.
I think this what you need:
var resultsDiv = document.getElementById('resultsDiv');
var resultsInnerDiv = document.getElementById('resultsInnerDiv');
resultsInnerDiv.innerHTML = "test";
var h = parseInt(resultsInnerDiv.offsetHeight);
alert(parseInt(resultsInnerDiv.offsetHeight));
resultsInnerDiv.style.top = ((h / 2) -125) + 'px';
offsetHeight is not a property of element.style, but the element itself (see here).
Try this:
var resultsDiv = document.getElementById('resultsDiv');
var resultsInnerDiv = document.getElementById('resultsInnerDiv');
resultsInnerDiv.innerHTML = "test";
var h = parseInt(resultsInnerDiv.offsetHeight);
alert(h);
resultsInnerDiv.style.top = ((h / 2) -125) + 'px';