Javascript width of span element - javascript

I create via a String some DOM-Elements and assign it via innerHTML to a div.
Now I need a flexible width of the span element (span_homebase). What I experience is that sometimes (for me it looks randomly) it returns other width than expected. I need the width of this span element because after this in the same line there is an Information Button but the width of the span could be different. So I use the width of span element as margin for the information button. Code is as follows. I searched for a async handler of innerHTML but in another post somebody said that it is not required because it is assigned instantly.
Are there any thoughts what to try? Because the information is now sometimes on the right place and sometimes not.
string = string + "<span id ='span_homebase'>" + homebaseCity + "</span>";
string = string + "<div class='section-end'></div>";
element.innerHTML = string;
var marginInfoHomebase = document.getElementById('span_homebase').offsetWidth + 20;
document.getElementById("information_button_homebase").style.margin = "5px 0 0 " + marginInfoHomebase + "px";
Now if I open the respective site for me sometimes the marginInfoHomebase sometimes returns 108 and sometimes 183 even if the same value is assigned to span element. Very strange. Any thoughts?
EDIT:
Working Example:
function createHomeSettingsView(settingsA){
var element = document.getElementById("table-settings");
var string = "";
for(var i = 0; i < settingsA.length; i++){
for(var z = 0; z < settingsA[i].length; z++){
if(i == 1){
//Notification buttons
if(z == 1){
//homebase setting
if(window.localStorage.getItem("switch_homebase") == 1){
//homebase on
var homebaseCity = settingsA[i][z] + ": " + window.localStorage.getItem("homebase_loc");
string = string + " \
<div class='row' style='height:100px;' id='settings-sec-"+ i +"-row-" + z +"'> \
<div class='text'> \
<span id='span_homebase'>" + homebaseCity + "</span> \
</div> \
<div onClick='homebaseInfoClick();' class='information_button' id='information_button_homebase'></div>\
<div id='handleAutoSwitcherHomebase'></div>\
<div id='showRadiusRangeHomebase'>Radius: 30km</div>\
<input class='sliders' id='changeRadiusRangeHomebase' type='range' min='5' max='100' step='5' oninput='handleChangeHomebase(this.value)' onchange='handleInputHomebase(this.value)' >\
</div>";
}
else{
//homebase off
string = string + " \
<div class='row' id='settings-sec-"+ i +"-row-" + z +"'> \
<div class='text'>\
<span id='span_homebase'>" + settingsA[i][z] + "</span> \
</div> \
<div onClick='homebaseInfoClick();' class='information_button' id='information_button_homebase'></div>\
<div id='handleAutoSwitcherHomebase'></div>\
</div>";
}
}
}
}
}
element.innerHTML = string;
var marginInfoHomebase = document.getElementById("span_homebase").offsetWidth + 25;
var marginText = "5px 0 0 " + marginInfoHomebase + "px";
console.log("Span: " + marginInfoHomebase);
document.getElementById("information_button_homebase").style.margin = marginText;
}
CSS:
.container .table-settings{
top: 64px;
position: absolute;
padding:0;
left: 0;
right: 0;
bottom:0;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
background-color: #ECEBF3;
}
.container .table-settings .row{
background-color: white;
padding-top: 14px;
height:50px;
border-bottom: 1px solid LightGray;
}
.container .table-settings .row .text{
margin-left: 15px;
color: black;
float: left;
}
#span_homebase{
display: inline-block;
}

To get an inline element's width use getBoundingClientRect method:
var mySpan = document.getElementById("mySpan");
var spanWidth = mySpan.getBoundingClientRect().width;
var spanHeight = mySpan.getBoundingClientRect().height;

By default a span element has no dimension because its CSS display property is inline.
If you want to give it a width, you should change that property to inline-block.
<style>
span {
display: inline-block ;
}
</style>
Now you can play with its dimensions.

Related

What am I doing wrong in scoping my function?

In this test case, I am using append.child with plain JavaScript to add 3 kinds of divs (blue, red, green) to a parent multiple times according to their corresponding button onclicks, then I am adding another child inside the added div with another button (innerButton).
My issue is that, the onclick function which is assigned to the innerbutton and is nested within the initial function, listens only to the very first appended div, and it adds the input (which is supposed to be added to the div I'm clicking on) to the last append element of its 'kind'.
I am doing something wrong with my scoping but I can't see it.
I just started studying JavaScript, so I am not familiar yet with libraries, jQuery etc.
var countBlue = 0;
var countRed = 0;
var countGreen = 0;
function addBlue() {
var addTo = document.getElementById('div1')
var blue = document.createElement("div");
blue.id = "blueDiv";
blue.innerHTML = "<input id=blueInput><button id=innerButtonBlue onclick=addInputs()>ADD INPUTS</button>";
addTo.appendChild(blue);
document.getElementById("innerButtonBlue").onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = '<input id="newInput" placeholder="NEW">';
blue.appendChild(newInput);
}
countBlue++;
}
function addRed() {
var addTo = document.getElementById('div1')
var red = document.createElement("div");
red.id = "redDiv";
red.innerHTML = "<input id=redInput><button id=innerButtonRed>ADD INPUTS</button>";
addTo.appendChild(red);
document.getElementById("innerButtonRed").onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = '<input id="newInput" placeholder="NEW">';
red.appendChild(newInput);
}
countRed++;
}
function addGreen() {
var addTo = document.getElementById('div1')
var green = document.createElement("div");
green.id = "greenDiv";
green.innerHTML = "<input id=greenInput><button id=innerButtonGreen>ADD INPUTS</button>";
addTo.appendChild(green)
document.getElementById("innerButtonGreen").onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = '<input id="newInput" placeholder="NEW">';
green.appendChild(newInput);
}
countGreen++;
}
function displayCounters() {
alert("Blue divs amount : " + parseInt(countBlue) + "\n" + " Red divs amount : " + parseInt(countRed) + "\n" + " Green divs amount : " + parseInt(countGreen) + "\n" + "\n" + " All together is : " + (parseInt(countBlue) + parseInt(countRed) + parseInt(countGreen)))
}
button {
margin-bottom: 10px;
}
#blueDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
#redDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
#greenDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
input {
text-align: center;
}
#innerButtonRed {
position: relative;
float: right;
}
#innerButtonBlue {
position: relative;
float: right;
}
#innerButtonGreen {
position: relative;
float: right;
}
#newInput {
margin-top: 2px;
width: 162px;
height: 23px;
}
#redInput {
background: red;
}
#blueInput {
background: blue;
}
#greenInput {
background: green;
}
<html>
<body>
<script src="test.js"></script>
<link rel="stylesheet" type="text/css" href="test.css">
<button onclick="addBlue()">BLUE</button>
<button onclick="addRed()">RED</button>
<button onclick="addGreen()">GREEN</button>
<button onclick="displayCounters()">COUNTERS</button>
<div id="div1"></div>
</body>
</html>
The first thing you need to know is that, although you can technically add the same id to multiple elements, it is bad practice to do so. The id of an element should be unique. If you need to apply the same style or target multiple elements with your code you should use class instead of id.
I think that's what is causing issues in your code.
Second, since you say you are learning, i think it would be good if you tried to make a single function to add the elements since the code is repeated in all of the three functions, except for the color.
Try making the function accept the color as a variable so you can reuse it for the three colors. Imagine if it was a hundred colors.
var countBlue = 0;
var countRed = 0;
var countGreen = 0;
function addBlue() {
var addTo = document.getElementById('div1')
var div = document.createElement("div");
countBlue++; //set the counter to one so ids don't start at zero
div.id = `blueDiv-${countBlue}`; //creates a unique id depending on the counter
div.classList = "blueDiv";
div.innerHTML = `<input id="blueInput-${countBlue}" class="blueInput"><button id="innerButtonBlue-${countBlue}" onclick="addInputs">ADD INPUTS</button>`;
addTo.appendChild(div);
document.getElementById(`innerButtonBlue-${countBlue}`).onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = `<input id="newInput-blue-${countBlue}" class="newInput" placeholder="NEW">`;
div.appendChild(newInput);
}
}
function addRed() {
var addTo = document.getElementById('div1')
var div = document.createElement("div");
countRed++
div.id = `redDiv-${countRed}`;
div.classList = "redDiv";
div.innerHTML = `<input id="redInput-${countRed}" class="redInput"><button id="innerButtonRed-${countRed}" onclick="addInputs">ADD INPUTS</button>`;
addTo.appendChild(div);
document.getElementById(`innerButtonRed-${countRed}`).onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = `<input id="newInput-red-${countRed}" class="newInput" placeholder="NEW">`;
div.appendChild(newInput);
}
}
function addGreen() {
var addTo = document.getElementById('div1')
var div = document.createElement("div");
countGreen++
div.id = `greenDiv-${countGreen}`;
div.classList = "greenDiv";
div.innerHTML = `<input id="greenInput-${countGreen}" class="greenInput"><button id="innerButtonGreen-${countGreen}" onclick="addInputs">ADD INPUTS</button>`;
addTo.appendChild(div);
document.getElementById(`innerButtonGreen-${countGreen}`).onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = `<input id="newInput-green-${countGreen}" class="newInput" placeholder="NEW">`;
div.appendChild(newInput);
}
}
function displayCounters() {
alert("Blue divs amount : " + parseInt(countBlue) + "\n" + " Red divs amount : " + parseInt(countRed) + "\n" + " Green divs amount : " + parseInt(countGreen) + "\n" + "\n" + " All together is : " + (parseInt(countBlue) + parseInt(countRed) + parseInt(countGreen)))
}
button {
margin-bottom: 10px;
}
.blueDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
.redDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
.greenDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
input {
text-align: center;
}
.innerButtonRed {
position: relative;
float: right;
}
.innerButtonBlue {
position: relative;
float: right;
}
.innerButtonGreen {
position: relative;
float: right;
}
.newInput {
margin-top: 2px;
width: 162px;
height: 23px;
}
.redInput {
background: red;
}
.blueInput {
background: blue;
}
.greenInput {
background: green;
}
<button onclick="addBlue()">BLUE</button>
<button onclick="addRed()">RED</button>
<button onclick="addGreen()">GREEN</button>
<button onclick="displayCounters()">COUNTERS</button>
<div id="div1"></div>
IDs need to be unique in the whole document. Don't use IDs for this, you can just use a class. But even with a class how is the code supposed to know which element it should look for since there will be more than one existing with the class? The solution is to search only inside the element that you just created (e.g. blue.querySelector('.someClass') to search for the first element with class someClass inside the blue element).
I changed your code to use classes everywhere:
var countBlue = 0;
var countRed = 0;
var countGreen = 0;
function addBlue() {
var addTo = document.getElementById('div1')
var blue = document.createElement("div");
blue.className = "blueDiv";
blue.innerHTML = "<input class='firstInput'><button class='innerButton'>ADD INPUTS</button>";
addTo.appendChild(blue);
blue.querySelector(".innerButton").onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = '<input class="newInput" placeholder="NEW">';
blue.appendChild(newInput);
}
countBlue++;
}
function addRed() {
var addTo = document.getElementById('div1')
var red = document.createElement("div");
red.className = "redDiv";
red.innerHTML = "<input class='firstInput'><button class='innerButton'>ADD INPUTS</button>";
addTo.appendChild(red);
red.querySelector(".innerButton").onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = '<input class="newInput" placeholder="NEW">';
red.appendChild(newInput);
}
countRed++;
}
function addGreen() {
var addTo = document.getElementById('div1')
var green = document.createElement("div");
green.className = "greenDiv";
green.innerHTML = "<input class='firstInput'><button class='innerButton'>ADD INPUTS</button>";
addTo.appendChild(green)
green.querySelector(".innerButton").onclick = function() {
var newInput = document.createElement("div");
newInput.innerHTML = '<input class="newInput" placeholder="NEW">';
green.appendChild(newInput);
}
countGreen++;
}
function displayCounters() {
alert("Blue divs amount : " + parseInt(countBlue) + "\n" + " Red divs amount : " + parseInt(countRed) + "\n" + " Green divs amount : " + parseInt(countGreen) + "\n" + "\n" + " All together is : " + (parseInt(countBlue) + parseInt(countRed) + parseInt(countGreen)))
}
button {
margin-bottom: 10px;
}
.blueDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
.redDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
.greenDiv {
margin-top: 10px;
margin-bottom: 10px;
width: 300px;
}
input {
text-align: center;
}
.redDiv .innerButton {
position: relative;
float: right;
}
.blueDiv .innerButton {
position: relative;
float: right;
}
.greenDiv .innerButton {
position: relative;
float: right;
}
.newInput {
margin-top: 2px;
width: 162px;
height: 23px;
}
.redDiv .firstInput {
background: red;
}
.blueDiv .firstInput {
background: blue;
}
.greenDiv .firstInput {
background: green;
}
body {
height: 800px; /* Just for visibility here in Stack Overflow */
}
<html>
<body>
<script src="test.js"></script>
<link rel="stylesheet" type="text/css" href="test.css">
<button onclick="addBlue()">BLUE</button>
<button onclick="addRed()">RED</button>
<button onclick="addGreen()">GREEN</button>
<button onclick="displayCounters()">COUNTERS</button>
<div id="div1"></div>
</body>
</html>
There is a lot more that could be improved of course - the code duplication could be removed, a generalized function for all three types (red/green/blue) can be created that is differentiated just by an attribute on the button for example - but that's beyond the scope of this answer.

Animating the percent of a "percentage bar" during the transition

I have created a very basic animated percentage bar with HTML, CSS, and JS the only problem is that I am trying to devise a way to also animate the increase and/or decrease of the percentage output to go along with an animated percentage bar. In the example below and in this JsFiddle I have successfully created that with the only problem being that it doesn't seem to be the most efficient or effective way of doing it.
In the code snippet directly below I'm creating this animated effect by...
Setting x equal to setInterval
Capturing the width of percent bar on the left and removing the px from the end of the string.
Capturing the width of percent bar on the right and removing the px from the end of the string.
Displays the percent value for the left (blue) bar inside the tooltip that can be seen when hovered over.
Displays the percent value for the right (red) bar inside the tooltip that can be seen when hovered over.
Displays the percent value of the left (blue) bar below the percent bar.
Displays the percent value of the right (red) bar below the percent bar.
All of this code below will run every 64 Milliseconds.
This code will only run for 2000 Milliseconds which is the same amount of time that I have set the transition for the percent bars.
Note: The whole point of the code below is to give the illusion that the percent values are increasing as either of the percent bars are increasing. In short, the goal is to make it seem more animated rather than the number all of a sudden seeing the number jump from one number to the next.
There just has to be a better way of achieving the same effect (or better) rather than pulling data from the DOM every 64 Milliseconds. There are tons of real-time graph's out on the web that achieve the same effect but I can't figure out how so I came up with my own and don't really think that they do it this way either. Any ideas??? I would only like to use pure Javascript with no libraries such as jQuery.
var x = setInterval(function() {
var left = parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
var right = parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
p_bar_left.querySelector('.percent-value').innerText = left.toFixed(2) + '%';
document.querySelector('#blue').querySelector('.percent-amount').innerText = left.toFixed(2) + '%';
p_bar_right.querySelector('.percent-value').innerText = right.toFixed(2) + '%';
document.querySelector('#red').querySelector('.percent-amount').innerText = right.toFixed(2) + '%';
}, 64);
setTimeout(function() {
clearInterval(x)
}, 2000);
var good = document.querySelector('#good');
var bad = document.querySelector('#bad');
var p_bar_left = document.querySelector('#progressbar-left');
var p_bar_right = document.querySelector('#progressbar-right');
var counter_left = 0;
var counter_right = 0;
var percent_left = 0;
var percent_right = 0;
function changePercent(increment, which) {
if (which == 'left') {
counter_left += increment;
} else if (which == 'right') {
counter_right += increment;
} else {
throw "Don't know which value to increase.";
}
percent_left = (counter_left / (counter_left + counter_right)) * 100;
percent_right = (counter_right / (counter_left + counter_right)) * 100;
p_bar_left.style.width = percent_left + '%';
p_bar_right.style.width = percent_right + '%';
document.querySelector('#total-amount').innerText = counter_right + counter_left;
var x = setInterval(function() {
var left = parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
var right = parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
p_bar_left.querySelector('.percent-value').innerText = left.toFixed(2) + '%';
document.querySelector('#blue').querySelector('.percent-amount').innerText = left.toFixed(2) + '%';
p_bar_right.querySelector('.percent-value').innerText = right.toFixed(2) + '%';
document.querySelector('#red').querySelector('.percent-amount').innerText = right.toFixed(2) + '%';
}, 64);
setTimeout(function() {
clearInterval(x)
}, 2000);
}
good.addEventListener('click', function() {
changePercent(1, 'left');
});
bad.addEventListener('click', function() {
changePercent(1, 'right');
});
var tooltip = document.querySelectorAll('.tooltip');
var tooltipelement = document.querySelectorAll('#progressbar-left, #progressbar-right');
for (var x = tooltipelement.length; x--;) {
tooltipelement[x].addEventListener('mousemove', function(e) {
for (var i = tooltip.length; i--;) {
tooltip[i].style.left = e.pageX + 20 + 'px';
tooltip[i].style.top = e.pageY + 'px';
}
});
}
#progressbar-container {
display: flex;
position: relative;
width: 50vw;
height: 32px;
border: 2px solid black;
background-color: #ccc;
justify-content: space-between;
}
#progressbar-left {
position: relative;
height: 100%;
background-color: blue;
transition: width 2s;
align-items: center;
justify-content: center;
}
#progressbar-right {
position: relative;
height: 100%;
background-color: red;
transition: width 2s;
align-items: center;
justify-content: center;
}
.tooltip {
display: none;
position: fixed;
width: auto;
height: auto;
padding: 6px;
background-color: black;
text-align: center;
border-radius: 6px;
z-index: 1;
}
.object {
display: inline-block;
color: #fff;
}
.percent-value {
display: inline-block;
color: #fff;
}
#progressbar-left:hover .tooltip {
display: block;
}
#progressbar-right:hover .tooltip {
display: block;
}
#total {
display: block;
font-weight: bold;
}
#total-amount {
display: inline-block;
font-weight: normal;
}
#blue,
#red {
display: block;
font-weight: bold;
}
.percent-amount {
display: inline-block;
font-weight: normal;
}
<body>
<input type="button" value="Good" id="good">
<input type="button" value="Bad" id="bad">
<div id="progressbar-container">
<div id="progressbar-left">
<div class="tooltip">
<span class="tooltiptext">
<span class="object">Blue</span>
<span class="percent-value"></span>
</span>
</div>
</div>
<div id="progressbar-right">
<div class="tooltip">
<span class="tooltiptext">
<span class="object">Red</span>
<span class="percent-value"></span>
</span>
</div>
</div>
</div>
<span id="total">Total: <p id="total-amount">0</p></span>
<span id="blue">Percent Blue: <p class="percent-amount">0%</p></span>
<span id="red">Percent Red: <p class="percent-amount">0%</p></span>
</body>
JsFiddle

Change amount from px to percent based

I am trying to create a carousel with a sliding effect. The basic setup is I have 2 div's wrapping around the sliding content. The first div's width (the outerWrapper) is the size of all slides width together. The next div's width (the innerWrapper) is the size of 1 div.
When it's supposed to change slides, the innerWrapper get's a translation of x amount and it animates from the css transition.
I have everything working, but there's one thing I want to change. I want to change from pixel to percent.
Line 29:
innerWrapper.style.transform = 'translateX(-' + imgWidth * targetIndex + 'px)';
I tried a lot of things, but nothing worked. The only thing that worked was targetIndex * 20 + %, but that only works for 5 divs. It's not a concrete solution. How can I make the translateX percentage based?
JSFiddle
var trigger = document.getElementsByClassName('trigger'),
outerWrapper = document.createElement('div'),
innerWrapper = document.createElement('div'),
slide = document.getElementsByClassName('slide'),
parentElm = slide[0].parentNode,
imgWidth = slide[0].offsetWidth,
lastElm = trigger.length - 1,
previousSelectedIndex = 0;
innerWrapper.id = 'innerWrapper';
outerWrapper.id = 'outerWrapper';
trigger[0].className += ' selected';
innerWrapper.style.width = imgWidth * (lastElm + 1) + 'px';
while (slide.length) {
innerWrapper.appendChild(slide[0]);
}
for (var i = 0; i < trigger.length; i++) {
trigger[i].addEventListener('click', function(e) {
clickEvent(e);
})
}
function clickEvent(e) {
if (!hasClass(e.target, 'selected')) {
var targetIndex = [].slice.call(trigger).indexOf(e.target);
innerWrapper.style.transform = 'translateX(-' + imgWidth * targetIndex + 'px)';
e.target.className += ' selected';
removeClass(trigger[previousSelectedIndex], 'selected');
previousSelectedIndex = targetIndex;
}
}
outerWrapper.appendChild(innerWrapper);
parentElm.appendChild(outerWrapper);
function hasClass(element, cls) {
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
ele.className = ele.className.replace(reg, ' ');
}
}
#outerWrapper {
float: left;
width: 270px;
height: 266px;
overflow: hidden;
}
#innerWrapper {
position: relative;
display: flex;
transition: transform 0.7s cubic-bezier(0.45, 0.05, 0.55, 0.95);
}
ul.triggers li {
float: left;
margin: 0 5px;
font: bold 16px arial;
cursor: pointer;
background-color: #ccc;
padding: 10px;
}
ul.triggers li.selected {
background-color: orange;
}
<img class="slide" width="270" src="http://i.imgur.com/XyWadkY.jpg" />
<img class="slide" width="270" src="http://i.imgur.com/OpP86hg.jpg" />
<img class="slide" width="270" src="http://i.imgur.com/oWbhwWT.jpg" />
<img class="slide" width="270" src="http://i.imgur.com/IXcqVB1.jpg" />
<img class="slide" width="270" src="http://i.imgur.com/OpP86hg.jpg" />
<ul class="triggers">
<li class="trigger">1</li>
<li class="trigger">2</li>
<li class="trigger">3</li>
<li class="trigger">4</li>
<li class="trigger">5</li>
</ul>
Try this,
innerWrapper.style.transform = 'translateX(-' + (targetIndex*(100/document.getElementsByClassName("slide").length)) + '%)';

Complex Jquery Selector With Variables

I am building a demo of a lotto site where a user is presented with one section full of the balls and a second section that will fill up with his/her selections.
What I am trying to do is create a jQuery function that will run when a user clicks on a ball, this function must retrieve the number of the ball that was clicked as well as the color of the ball (background-image) and then set the number and the background-image to the next available ball.
Here is a jsFiddle: http://jsfiddle.net/8kq5p6gb/1/
This is my jQuery function, it stores the number and background of the clicked ball and then tries to find the next available ball open and applies that text and background to it but it currently does not work. When I click on a ball I get this error :
Uncaught Error: Syntax error, unrecognized expression: [object Object] a:nth-child(1)
For Code :
$(document).ready(function () {
var line_counter = 1;
var number_counter = 1;
$('.draw-selection-wrapper.choice .draw-number').click(function (e) {
event.preventDefault(e);
var number = $(this).text(); ;
var background = $(this).css('background-image');
var row = $('.draw-selection-wrapper.selections div:nth-child(' + line_counter + ')');
var link = $(row + ' a:nth-child(' + number_counter + ')');
link.text(number);
link.css('background-image', background);
number_counter = number_counter + 1;
if (number_counter == 8) {
line_counter = line_counter + 1;
number_counter = 1;
}
});
});
Here is my HTML:
<div class="draw-numbers-outer-wrapper">
<div class="draw-selection-wrapper choice">
<div class="draw-number-row one">
1
2
3
4
5
6
7
</div>
<div class="draw-number-row two">
8
9
10
11
12
13
14
</div>
</div>
<div class="draw-selection-wrapper selections">
<div class="draw-number-row">
</div>
<div class="draw-number-row">
</div>
</div>
</div>
And my CSS:
.draw-selection-wrapper {
width: 50%;
float: left;
}
.draw-number-row {
height: 36px;
border: 1px solid #C6C4C5;
padding-left: 10px;
}
.line-number {
float: left;
height: 100%;
width: 12px;
}
.draw-number {
width: 9%;
float: left;
height: 100%;
line-height: 36px;
margin: 0px 2.5% 0px 2.5%;
color: #000;
text-align: center;
background-color: green;
}
.draw-selection-wrapper.selections .draw-number {
margin: 0px 2% 0px 2%;
}
.draw-number-row.one .draw-number {
background-color: red;
}
.draw-number-row.two .draw-number {
background-color: teal;
}
row is a jQuery object, not a string, and as such you end up with [Object, object] when you try to concantenate the object into a string.
You could use the context selector instead
var row = $('.draw-selection-wrapper.selections div:nth-child(' + line_counter + ')');
var link = $('a:nth-child(' + number_counter + ')', row);
FIDDLE
var link = row.find('a:nth-child(' + number_counter + ')');
Fiddle
replace this
var row = $('.draw-selection-wrapper.selections div:nth-child(' + line_counter + ')');
var link = $(row + ' a:nth-child(' + number_counter + ')');
by
var row = '.draw-selection-wrapper.selections div:nth-child(' + line_counter + ')';
var link = $(row + ' a:nth-child(' + number_counter + ')');

Assigning a value to innerHTML not working

Looking for a second set of eyes here...
I am calling this function:
customPanel(map, "map2", dirn, document.getElementById("path2"), 1);
In customPanel, I am then building html, then trying to assign it to the page:
Here is the function, the innerHTML is near the very bottom. If I throw an alert before I try to assign the html to the innerHTML of div, it alerts correctly:
<script type="text/javascript">
var map = "";
function customPanel(map, mapname, dirn, div) {
var html = "";
function waypoint(point, type, address) {
var target = '"' + mapname + ".showMapBlowup(new GLatLng(" + point.toUrlValue(6) + "))" + '"';
html += '<table style="border: 1px solid silver; margin: 10px 0px; background-color: rgb(238, 238, 238); border-collapse: collapse; color: rgb(0, 0, 0);">';
html += ' <tr style="cursor: pointer;" onclick=' + target + '>';
html += ' <td style="padding: 4px 15px 0px 5px; vertical-align: middle; width: 20px;">';
html += ' <img src="http://maps.gstatic.com/intl/en_us/mapfiles/marker_green' + type + '.png">'
html += ' <\/td>';
html += ' <td style="vertical-align: middle; width: 100%;">';
html += address;
html += ' <\/td>';
html += ' <\/tr>';
html += '<\/table>';
}
function routeDistance(dist) {
html += '<div style="text-align: right; padding-bottom: 0.3em;">' + dist + '<\/div>';
}
function detail(point, num, description, dist) {
var target = '"' + mapname + ".showMapBlowup(new GLatLng(" + point.toUrlValue(6) + "))" + '"';
html += '<table style="margin: 0px; padding: 0px; border-collapse: collapse;">';
html += ' <tr style="cursor: pointer;" onclick=' + target + '>';
html += ' <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; text-align: right;">';
html += ' <a href="javascript:void(0)"> ' + num + '. <\/a>';
html += ' <\/td>';
html += ' <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; width: 100%;">';
html += description;
html += ' <\/td>';
html += ' <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px 0.3em 0.5em; vertical-align: top; text-align: right;">';
html += dist;
html += ' <\/td>';
html += ' <\/tr>';
html += '<\/table>';
}
function copyright(text) {
html += '<div style="font-size: 0.86em;">' + text + "<\/div>";
}
// === read through the GRoutes and GSteps ===
for (var i = 0; i < dirn.getNumRoutes(); i++) {
if (i == 0) {
var type = "A";
} else {
var type = "B";
}
var route = dirn.getRoute(i);
var geocode = route.getStartGeocode();
var point = route.getStep(0).getLatLng();
// === Waypoint at the start of each GRoute
waypoint(point, type, geocode.address);
routeDistance(route.getDistance().html + " (about " + route.getDuration().html + ")");
for (var j = 0; j < route.getNumSteps(); j++) {
var step = route.getStep(j);
// === detail lines for each step ===
detail(step.getLatLng(), j + 1, step.getDescriptionHtml(), step.getDistance().html);
}
}
// === the final destination waypoint ===
var geocode = route.getEndGeocode();
var point = route.getEndLatLng();
waypoint(point, "B", geocode.address);
// === the copyright text ===
copyright(dirn.getCopyrightsHtml());
// === drop the whole thing into the target div
div.innerHTML = html;
}
</script>
EDIT:
Here is the HTML as requested. It's just two divs:
<div class="mapWrapper">
<div id="path2"> </div>
<div id="map2"> </div>
</div>
To clarify, the path2 and map2 are being generated dynamically by looping through $_POST values in PHP. Here is a snippit:
foreach($post_entries as $e){
echo "
<div class=\"mapWrapper\">
<div id=\"path" . $increased_counter ."\"> </div>
<div id=\"map" . $increased_counter ."\"> </div>
</div>";
}
EDIT #2
As requested by #user1090190, a public version of the page:
http://qxxiv6yc.myutilitydomain.com/trip-planned
I suspect that path2 has not already been loaded when you call that function. It doesn't matter that you're generating it via PHP. You have to wait for the DOM to load in the browser first before you can reference specific elements. There are two solutions to this:
Call the function after page load. I.e.
<body onload="customPanel(map, "map2", dirn, document.getElementById("path2"), 1);">
Put all your Javascript at the bottom

Categories