Chart JS Show HTML in Tooltip - javascript

I've been fighting with Chart JS's documentation trying to figure out how to modify the content of a line chart's tool tip when you hover over a specific point.
Basically, I want to display the values on all the same vertical axis whenever a single point is hovered over. I've tried something like this:
tooltips: {
callbacks: {
label: function(tooltipItem, data){
console.log(data);
var html = "";
for(var dataset in data.datasets){
html += "<label>" + data.datasets[dataset].label + ": " + data.datasets[dataset].data[tooltipItem.index] + "%</label><br/>";
}
return html;
}
},
},
This works to the degree of looping over each data set and appending <label>Example: 0%<br/></label> for each dataset, but when I return that HTML, the tooltip literally displays the string:
<label>Example1: 1%</label><br/><label>Example2: 5%</label><br/> ...
Instead of rendering the correct HTML:
Example1: 1%
Example2: 5%
...
Now, I know that Chart JS version 1.0 has the tooltipTemplate option, but I can't seem to figure out if there is any way to return HTML in the tooltips.callbacks.label option. There's documentation for how to do custom tooltips, which I will end up using if I can't figure this out, but any help would be appreciated.

As of v2.4, the callbacks unfortunately don't allow for HTML currently. You'll need to write a custom tooltip function.
Examples can be found in the samples folder for chart-js (although some are better than others I found).
https://github.com/chartjs/Chart.js/tree/v2.4.0/samples/tooltips
Try running the samples to get a feel for how the options and modifications affect the tooltip function.
For example in the line chart example of a custom function:
Chart.defaults.global.pointHitDetectionRadius = 1;
var customTooltips = function(tooltip) {
// Tooltip Element
var tooltipEl = document.getElementById('chartjs-tooltip');
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = "<table></table>"
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
if (tooltip.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltip.yAlign) {
tooltipEl.classList.add(tooltip.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
function getBody(bodyItem) {
return bodyItem.lines;
}
// Set Text
if (tooltip.body) {
var titleLines = tooltip.title || [];
var bodyLines = tooltip.body.map(getBody);
//PUT CUSTOM HTML TOOLTIP CONTENT HERE (innerHTML)
var innerHtml = '<thead>';
titleLines.forEach(function(title) {
innerHtml += '<tr><th>' + title + '</th></tr>';
});
innerHtml += '</thead><tbody>';
bodyLines.forEach(function(body, i) {
var colors = tooltip.labelColors[i];
var style = 'background:' + colors.backgroundColor;
style += '; border-color:' + colors.borderColor;
style += '; border-width: 2px';
var span = '<span class="chartjs-tooltip-key" style="' + style + '"></span>';
innerHtml += '<tr><td>' + span + body + '</td></tr>';
});
innerHtml += '</tbody>';
var tableRoot = tooltipEl.querySelector('table');
tableRoot.innerHTML = innerHtml;
}
var position = this._chart.canvas.getBoundingClientRect();
// Display, position, and set styles for font
tooltipEl.style.opacity = 1;
tooltipEl.style.left = position.left + tooltip.caretX + 'px';
tooltipEl.style.top = position.top + tooltip.caretY + 'px';
tooltipEl.style.fontFamily = tooltip._fontFamily;
tooltipEl.style.fontSize = tooltip.fontSize;
tooltipEl.style.fontStyle = tooltip._fontStyle;
tooltipEl.style.padding = tooltip.yPadding + 'px ' + tooltip.xPadding + 'px';
};
Then set this as the custom tooltip function in the options for the chart:
window.myLine = new Chart(chartEl, {
type: 'line',
data: lineChartData,
options: {
title:{
display:true,
text:'Chart.js Line Chart - Custom Tooltips'
},
tooltips: {
enabled: false,
mode: 'index',
position: 'nearest',
//Set the name of the custom function here
custom: customTooltips
}
}
});
EDIT: Apologies, I only read the title of your question, not the full question. What you ask can be done more simply and without HTML in the tooltips (unless it's required for another reason) by changing the interaction mode to index in the options. There's a sample available to show how this works.

Good, although with the previous solution to solve the problem I think that the solution offered by chart.js is a bit ... Confusing. The same can be applied in a more understandable way. Based on the chart.js guide I have created an HTML that will be used in the tooltip. What I got was the following:
First I give you the code used and then I explain it:
tooltip: {
// Disable the on-canvas tooltip
enabled: false,
external: (context) => {
// Tooltip Element
let tooltipEl = document.getElementById('chartjs-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = '<table></table>';
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
const tooltipModel = context.tooltip;
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = '0';
return;
}
// Set caret Position (above, below,no-transform ).As I need above I don't delete that class
tooltipEl.classList.remove('below', 'no-transform');
// Set HTML & Data
if (tooltipModel.body) {
const dataFromCurrentElement = tooltipModel.dataPoints[0];
const currentElement = dataFromCurrentElement.dataIndex;
const formattedValue = dataFromCurrentElement.formattedValue.trim();
const currentDataToShow = formattedValue.substr(1, formattedValue.length - 2).split(' ');
const innerHtml = `
<div style="border-collapse: separate; overflow: hidden; border-radius: 10px; box-shadow: 0 6px 12px rgba(0,0,0,.175);">
<div style="background-color: #ECEFF1; padding-top: 5px; padding-bottom: 6px; padding-left: 7px; color: #000; font-family: 'Poppins'; font-size: 14px; border-bottom: solid 1px #DDD">
Name
</div>
<div style="display: flex; padding: 1.2rem; background-color: white">
<div style="display: flex; margin-right: 1.2rem;align-items: center; ">
<div style="border-radius: 100%; background-color: #6785C1; height: 13px; width: 13px;"></div>
</div>
<div style="display: flex; flex-direction: column; font-family: 'Poppins'; font-size: 14px">
<div>Revenue: <span style="font-weight: 600">${currentDataToShow[0].substr(0, currentDataToShow[0].length - 1)}</span></div>
<div>Revenue per employee: <span style="font-weight: 600">${currentDataToShow[1].substr(0, currentDataToShow[1].length - 1)}</span></div>
<div>Net income per employee: <span style="font-weight: 600">${this.customReportUtilities.parseNumberFunction(Number(currentDataToShow[2]) * 100)}</span></div>
</div>
</div>
</div>
`;
tooltipEl.querySelector('table').innerHTML = innerHtml;
}
const position = context.chart.canvas.getBoundingClientRect();
// Display, position, and set styles for font
tooltipEl.style.opacity = '1';
tooltipEl.style.position = 'absolute';
tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + 'px';
tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + 'px';
tooltipEl.style.padding = tooltipModel.padding + 'px ' + tooltipModel.padding + 'px';
tooltipEl.style.pointerEvents = 'none';
}
}
This is the same code, it is not necessary to duplicate it, with the above it is worth
We hide the tooltip from chartjs using tooltip false, then in external we pass the function to use our HTML as tooltip
let tooltipEl = document.getElementById('chartjs-tooltip');
We collect the container with id chartjs-tooltip, if it does not exist (the mouse had not been placed on the graph) we create it (it is the following if).
let tooltipEl = document.getElementById('chartjs-tooltip');
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = '<table></table>';
document.body.appendChild(tooltipEl);
}
We hide the tooltip when the user does not have the cursor over an element (this is because otherwise it would always be seen.
const tooltipModel = context.tooltip; if (tooltipModel.opacity === 0) { tooltipEl.style.opacity = '0'; return; }
We indicate the position of the tooltip, to choose between above, below or no-transform. I have removed all but above because it is the class I want to keep.
tooltipEl.classList.remove('below', 'no-transform');
We get the data for the current element and form the HTML with its styles ... Saving it in a variable as a string and we pass it our string with the HTML.
if (tooltipModel.body) {
const dataFromCurrentElement = tooltipModel.dataPoints[0];
const currentElement = dataFromCurrentElement.dataIndex;
const formattedValue = dataFromCurrentElement.formattedValue.trim();
const currentDataToShow = formattedValue.substr(1, formattedValue.length - 2).split(' ');
const innerHtml = `
<div style="border-collapse: separate; overflow: hidden; border-radius: 10px; box-shadow: 0 6px 12px rgba(0,0,0,.175);"> <div style="background-color: #ECEFF1; padding-top: 5px; padding-bottom: 6px; padding-left: 7px; color: #000; font-family: 'Poppins'; font-size: 14px; border-bottom: solid 1px #DDD">
Name
</div>
<div style="display: flex; padding: 1.2rem; background-color: white">
<div style="display: flex; margin-right: 1.2rem;align-items: center; ">
<div style="border-radius: 100%; background-color: #6785C1; height: 13px; width: 13px;"></div>
</div>
<div style="display: flex; flex-direction: column; font-family: 'Poppins'; font-size: 14px">
<div>Revenue: <span style="font-weight: 600">${currentDataToShow[0].substr(0, currentDataToShow[0].length - 1)}</span></div>
<div>Revenue per employee: <span style="font-weight: 600">${currentDataToShow[1].substr(0, currentDataToShow[1].length - 1)}</span></div>
<div>Net income per employee: <span style="font-weight: 600">${this.customReportUtilities.parseNumberFunction(Number(currentDataToShow[2]) * 100)}</span></div>
</div>
</div>
</div>
`;
tooltipEl.querySelector('table').innerHTML = innerHtml;
}
Finally we finish configuring the container
const position = context.chart.canvas.getBoundingClientRect();
// Display, position, and set styles for font
tooltipEl.style.opacity = '1';
tooltipEl.style.position = 'absolute';
tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + 'px';
tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + 'px';
tooltipEl.style.padding = tooltipModel.padding + 'px ' + tooltipModel.padding + 'px';
tooltipEl.style.pointerEvents = 'none';
I have had to use some additional styles as an overflow to make the border-radius show. Chart.js documentation on the subject: https://www.chartjs.org/docs/latest/samples/tooltip/html.html

Related

How to create a scrollable/touchable grid in CSS and JS?

I'm interested in how I can make a grid with an undetermined amount of columns and rows that I can put inside another div and have it not spill into others objects or mess with the parent size.
I want it to be square and I'm using Tailwind CSS but I can adapt to SCSS or vanilla CSS. Also I want it to be touchable/moveable with a mouse on desktop and touch capable devices.
How would I go about accomplishing this?
Assuming I've understood your question correctly, here is one way you could do it. I haven't tested it with a touch device but it shouldn't be hard to modify it to also respond to touch events.
const items = [
['a0', 'a1', 'a2'],
['b0', 'b1', 'b2'],
['c0', 'c1', 'c2']
];
let html = '';
for (let rowItems of items) {
html += '<div class="row">';
for (let item of rowItems) {
html += '<div class="item">';
html += item;
html += '</div>';
}
html += '</div>';
}
const viewElem = document.querySelector('#view');
const outputElem = document.querySelector('#output');
outputElem.innerHTML = html;
let mouseStartPos = null;
let startOffset = null;
outputElem.addEventListener('mousedown', e => {
outputElem.classList.remove('animate');
mouseStartPos = {
x: e.clientX,
y: e.clientY
};
startOffset = {
x: outputElem.offsetLeft - viewElem.offsetLeft,
y: outputElem.offsetTop - viewElem.offsetTop
};
});
window.addEventListener('mouseup', e => {
mouseStartPos = null;
startOffset = null;
outputElem.classList.add('animate');
const xGridOffset = -1 * Math.max(0, Math.min(Math.round((outputElem.offsetLeft - viewElem.offsetLeft) / -100), items.length - 1));
const yGridOffset = -1 * Math.max(0, Math.min(Math.round((outputElem.offsetTop - viewElem.offsetTop) / -100), items[0].length - 1));
outputElem.style.left = `${xGridOffset * 100}px`;
outputElem.style.top = `${yGridOffset * 100}px`;
});
window.addEventListener('mousemove', e => {
if (mouseStartPos) {
const xOffset = mouseStartPos.x - e.clientX;
const yOffset = mouseStartPos.y - e.clientY;
outputElem.style.left = `${-1 * xOffset + startOffset.x}px`;
outputElem.style.top = `${-1 * yOffset + startOffset.y}px`;
}
});
#view {
width: 100px;
height: 100px;
overflow: hidden;
border: 2px solid blue;
}
#output {
position: relative;
}
.row {
display: flex;
}
.item {
display: flex;
min-width: 100px;
width: 100px;
height: 100px;
box-sizing: border-box;
justify-content: center;
align-items: center;
border: 1px solid red;
}
#output.animate {
transition: left 1s ease 0s, top 1s ease 0s;
}
Drag it!<br/>
<br/>
<div id="view">
<div id="output"></div>
</div>

Why is the distance between first and last element decreasing?

I'm trying to make an image slider. But as you can see the distance between the first and last element is not consistent. If you keep on dragging to left, the distance decreases and if you keep on dragging to right, the distance increases. Looks like the code is behaving differently on different zoom levels (sometimes?) and hence distance between every elements is changing at times.
//project refers to placeholder rectangular divs
projectContainer = document.querySelector(".project-container")
projects = document.querySelectorAll(".project")
elementAOffset = projects[0].offsetLeft;
elementBOffset = projects[1].offsetLeft;
elementAWidth = parseInt(getComputedStyle(projects[0]).width)
margin = (elementBOffset - (elementAOffset + elementAWidth))
LeftSideBoundary = -(elementAWidth)
RightSideBoundary = (elementAWidth * (projects.length)) + (margin * (projects.length))
RightSidePosition = RightSideBoundary - elementAWidth;
initialPosition = 0; //referring to mouse
mouseIsDown = false
projectContainer.addEventListener("mousedown", e => {
mouseIsDown = true
initialPosition = e.clientX;
})
projectContainer.addEventListener("mouseup", e => {
mouseExit(e)
})
projectContainer.addEventListener("mouseleave", e => {
mouseExit(e);
})
function mouseExit(e) {
mouseIsDown = false
//updates translateX value of transform
projects.forEach(project => {
var style = window.getComputedStyle(project)
project.currentTranslationX = (new WebKitCSSMatrix(style.webkitTransform)).m41
project.style.transform = 'translateX(' + (project.currentTranslationX) + 'px)'
})
}
projectContainer.addEventListener("mousemove", e => {
if (!mouseIsDown) { return };
// adds mousemovement to translateX
projects.forEach(project => {
project.style.transform = 'translateX(' + ((project.currentTranslationX ?? 0) + (e.clientX - initialPosition)) + 'px)'
shiftPosition(e, project)
})
})
//teleports div if it hits left or right boundary to make an infinite loop
function shiftPosition(e, project) {
projectStyle = window.getComputedStyle(project)
projectTranslateX = (new WebKitCSSMatrix(projectStyle.webkitTransform)).m41
//projectVisualPosition is relative to the left border of container div
projectVisualPosition = project.offsetLeft + projectTranslateX
if (projectVisualPosition <= LeftSideBoundary) {
project.style.transform = "translateX(" + ((RightSidePosition - project.offsetLeft)) + "px)"
updateTranslateX(e);
}
if (projectVisualPosition >= RightSidePosition) {
newPosition = -1 * (project.offsetLeft + elementAWidth)
project.style.transform = "translateX(" + newPosition + "px)"
updateTranslateX(e);
}
}
function updateTranslateX(e) {
projects.forEach(project => {
style = window.getComputedStyle(project)
project.currentTranslationX = (new WebKitCSSMatrix(style.webkitTransform)).m41
project.style.transform = 'translateX(' + (project.currentTranslationX) + 'px)'
initialPosition = e.clientX
})
}
*, *::before, *::after{
margin:0px;
padding:0px;
box-sizing: border-box;
font-size:0px;
user-select: none;
}
.project-container{
font-size: 0px;
position: relative;
width:1500px;
height:400px;
background-color: rgb(15, 207, 224);
margin:auto;
margin-top:60px;
white-space: nowrap;
overflow: hidden;
padding-left:40px;
padding-right:40px;
}
.project{
font-size:100px;
margin:40px;
display: inline-block;
height:300px;
width:350px;
background-color:red;
border: black 3px solid;
user-select: none;
}
<div class="project-container">
<div class="project">1</div>
<div class="project">2</div>
<div class="project">3</div>
<div class="project">4</div>
<div class="project">5</div>
<div class="project">6</div>
<div class="project">7</div>
<div class="project">8</div>
</div>
I'm not sure exactly how you would go about fixing your implementation. I played around with it for a while and discovered a few things; dragging more quickly makes the displacement worse, and the displacement seems to happen mainly when the elements are teleported at each end of the container.
I would guess that the main reason for this is that you are looping over all the elements and spacing them individually. Mouse move events generally happen under 20ms apart, and you are relying on all the DOM elements being repainted with their new transform positions before the next move is registered.
I did come up with a different approach using absolutely placed elements and the IntersectionObserver API, which is now supported in all modern browsers. The idea here is basically that when each element intersects with the edge of the container, it triggers an array lookup to see if the next element in the sequence is on the correct end and moves it there if not. Elements are only ever spaced by a static variable, while the job of sliding them is passed up to a new parent wrapper .project-slider.
window.addEventListener('DOMContentLoaded', () => {
// Style variables
const styles = {
width: 350,
margin: 40
};
const space = styles.margin*2 + styles.width;
// Document variables
const projectContainer = document.querySelector(".project-container");
const projectSlider = document.querySelector(".project-slider");
const projects = Array.from(document.querySelectorAll(".project"));
// Mouse interactions
let dragActive = false;
let prevPos = 0;
projectContainer.addEventListener('mousedown', e => {
dragActive = true;
prevPos = e.clientX;
});
projectContainer.addEventListener('mouseup', () => dragActive = false);
projectContainer.addEventListener('mouseleave', () => dragActive = false);
projectContainer.addEventListener('mousemove', e => {
if (!dragActive) return;
const newTrans = projectSlider.currentTransX + e.clientX - prevPos;
projectSlider.style.transform = `translateX(${newTrans}px)`;
projectSlider.currentTransX = newTrans;
prevPos = e.clientX;
});
// Generate initial layout
function init() {
let workingLeft = styles.margin;
projects.forEach((project, i) => {
if (i === projects.length - 1) {
project.style.left = `-${space - styles.margin}px`;
} else {
i !== 0 && (workingLeft += space);
project.style.left = `${workingLeft}px`;
};
});
projectSlider.currentTransX = 0;
};
// Intersection observer
function observe() {
const callback = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Find intersecting edge
const { left } = entry.boundingClientRect;
const isLeftEdge = left < projectContainer.clientWidth - left;
// Test and reposition next element
const targetIdx = projects.findIndex(project => project === entry.target);
let nextIdx = null;
const nextEl = () => projects[nextIdx];
const targetLeft = parseInt(entry.target.style.left);
const nextLeft = () => parseInt(nextEl().style.left);
if (isLeftEdge) {
nextIdx = targetIdx === 0 ? projects.length-1 : targetIdx - 1;
nextLeft() > targetLeft && (nextEl().style.left = `${targetLeft - space}px`);
} else {
nextIdx = targetIdx === projects.length-1 ? 0 : targetIdx + 1;
nextLeft() < targetLeft && (nextEl().style.left = `${targetLeft + space}px`);
};
};
});
};
const observer = new IntersectionObserver(callback, {root: projectContainer});
projects.forEach(project => observer.observe(project));
};
init();
observe();
});
*, *::before, *::after{
margin:0px;
padding:0px;
box-sizing: border-box;
font-size:0px;
user-select: none;
}
.project-container {
font-size: 0px;
width: 100%;
height: 400px;
background-color: rgb(15, 207, 224);
margin:auto;
margin-top:60px;
white-space: nowrap;
overflow: hidden;
}
.project-slider {
position: relative;
}
.project {
font-size:100px;
display: block;
position: absolute;
top: 40px;
height:300px;
width:350px;
background-color:red;
border: black 3px solid;
user-select: none;
}
<div class="project-container">
<div class="project-slider">
<div class="project">1</div>
<div class="project">2</div>
<div class="project">3</div>
<div class="project">4</div>
<div class="project">5</div>
<div class="project">6</div>
<div class="project">7</div>
<div class="project">8</div>
</div>
</div>
There is still an issue here which is how to resize the elements for smaller screens, and on browser resizes. You would have to add another event listener for window resizes which resets the positions and styles at certain breakpoints, and also determine the style variables programmatically when the page first loads. I believe this would still have been a partial issue with the original implementation so you'd have to address it at some point either way.

DOM assign an id to child Javascript

I have created a grid with div, class and id. I want to randomly create a yellow square and I want to assign an id= 'yellowSquare' how do I do it?
var grid = document.getElementById("grid-box");
for (var i = 1; i <= 100; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
var playerOne = [];
while (playerOne.length < 1) {
var randomIndex = parseInt(99 * Math.random());
if (playerOne.indexOf(randomIndex) === -1) {
playerOne.push(randomIndex);
var drawPone = document.getElementById('square' + randomIndex);
drawPone.style.backgroundColor = 'yellow';
}
}
#grid-box {
width: 400px;
height: 400px;
margin: 0 auto;
font-size: 0;
position: relative;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
<div id="grid-box"></div>
I am new to Javascript / jQuery. Any help will be much appreciated ! Thank you
There are two options to your question. You can either change the id of the yellow square which is already created from your code, or create a child element within the square, which looks the same as your current solution. Creating a new child element will let you keep the numeric id pattern for the grid:
Changing the ID :
var element = document.getElementById('square' + randomIndex)
element.id = "yellowSquare";
Adding new element inside:
var node = document.createElement("DIV");
node.id = "yellowSquare";
node.style = "background-color:yellow;height:100%;width:100%;";
var element = document.getElementById('square' + randomIndex)
element.appendChild(node);
I set the styling of the div child to 100% width and height, as it has no content, and would get 0 values if nothing was specified. This should make it fill the parent container.
There are also multiple other ways to achieve the same result, for instance with JQuery.
Use the HTMLElement method setAttribute (source);
...
var drawPone = document.getElementById('square' + randomIndex);
drawPone.style.backgroundColor = 'yellow';
drawPone.setAttribute('id', 'yellowSquare');
...
As you requested in your comment how to move the square i made an example how you can move it left and right using jQuery next() and prev() functions. However because your html elements are 1 dimensional it's not easy to move them up/down and check the sides for collisions. Better would be to create your html table like with rows and columns and this way create a 2 dimensional play field.
Also added a yellowSquery class for selection with $drawPone.addClass('yellowSquare');.
Also since you like to use jQuery I changed your existing code to jQuery function. Might help you learn the framework.
var $grid = $("#grid-box");
for (var i = 1; i <= 100; i++) {
var $square = $("<div>");
$square.addClass('square');
$square.attr('id','square' + i);
$grid.append($square);
}
var playerOne = [];
while (playerOne.length < 1) {
var randomIndex = parseInt(99 * Math.random());
if (playerOne.indexOf(randomIndex) === -1) {
playerOne.push(randomIndex);
var $drawPone = $('#square' + randomIndex);
$drawPone.addClass('yellowSquare');
}
}
$('#button_right').on('click', function(){
$yellowSquare = $('.yellowSquare')
$yellowSquareNext = $yellowSquare.next();
$yellowSquare.removeClass('yellowSquare');
$yellowSquareNext.addClass('yellowSquare');
});
$('#button_left').on('click', function(){
$yellowSquare = $('.yellowSquare')
$yellowSquarePrev = $yellowSquare.prev();
$yellowSquare.removeClass('yellowSquare');
$yellowSquarePrev.addClass('yellowSquare');
});
#grid-box {
width: 400px;
height: 400px;
margin: 0 auto;
font-size: 0;
position: relative;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
.yellowSquare {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="grid-box"></div>
<button id="button_left">left</button>
<button id="button_right">right</button><br>

Why can't i create divs and appenidng divs in a loop with javascript?

So I'm using this jquery plugin to have turnable cards. They're made by a div with the class card which has two appending divs with the class front respectively back. Each card has a different id (card-1, card2 etc.) witch different x and y coordinates, because, they're slightly offset, so you can see the edge of the next card.
So this function is to make the cards "flippable":
$(function(){
$(".card").flip({
axis: "y",
reverse: "false",
trigger: "click",
speed: 700,
front: 'autostrict',
back: 'autostrict'
});
});
And for the loop i got this script:
var i = 1;
var l = 10;
var t = -238;
var z = 2;
//nrFragen is gotten from a db, at the moment it equals 2
while (i <= (nrFragen)) {
var newDiv = document.createElement('div');
var frontDiv = document.createElement('div');
var backDiv = document.createElement('div');
newDiv.className = 'card';
newDiv.id = 'card-' + i;
newDiv.style.left = l + "px";
newDiv.style.top = t + "px";
newDiv.style.zIndex = z;
frontDiv.className = 'front';
backDiv.className = 'back';
newDiv.appendChild(frontDiv);
frontDiv.innerHTML = frontDiv.innerHTML + "Front";
newDiv.appendChild(backDiv);
backDiv.innerHTML = backDiv.innerHTML + "Back";
document.getElementsByTagName('body')[0].appendChild(newDiv);
i++;
l += 10;
t -= 10;
z++;
}
And this is the CSS:
.card {
position: absolute;
width: 400px;
height: 248px;
}
.front, .back {
background-color: #F3ECE2;
border: 5px blue solid;
padding: 10px;
border-radius: 25px;
}
#card-0{
left: 0px;
top: 0px;
z-index: 1;
}
There is allready a card-0 which is set with
<div class="card" id="card-0">
<div class="front">
Kategorie 1
</div>
<div class="back">
Alle Kategorien
</div>
If I'm running it nothing's happening, so what did I do wrong?

JavaScript can't write in element.style.position

I'm a complete newbie in javascript.
I make a function, that adds a div with id="test_div". After call it creates div in body and give an id to the element. After that i try to write style "element.style.position" and it doesn't work. But i can write a style in this element through "element.style.cssText". I tried to solve this by adding a variable with "window.getElementById()" after create the element, but it also doesn't work.
I don't understand what i am doing wrong. Hope for your help. Thank you.
Sorry for bad English.
html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="test.js">
</script>
</head>
<body>
<button onclick="add('Clicked ', 0)">Click</button>
</body>
</html>
js file:
var element_id = "test_div";
var default_timeout = 3;
var element_bg_color = "rgba(0, 0, 0, .5)";
var element_font_color = "#fff";
var element_pointer_events = "none";
var element_position = "fixed";
var element_top = "0";
var element_left = "0";
var element_padding = '.3em .6em';
var element_margin = "0";
var element_border_radius = "0 0 5px 0";
var add = function(string, timeout){
if(typeof(timeout) === 'undefined'){
timeout = default_timeout;
}
var element = document.createElement('div');
element.id = element_id;
element.style.position = "fixed";
element.style.cssText = "top: 0; left: 0; background-color: " + element_bg_color + "; margin: 0; padding: .3em .6em; border-radius: 0 0 5px 0; color: " + element_font_color + "; pointer-events: " + element_pointer_events + ";";
element.innerHTML = string;
if(document.getElementById(element_id) === null){
document.body.appendChild(element);
}else{
document.body.removeChild(element);
document.body.appendChild(element);
}
if(timeout > 0){
timeout *= 1000;
setTimeout(function() {
document.body.removeChild(element);
}, timeout);
}
};
By setting the cssText you are overwriting all the other styles
As the below example shows even though I set the fontSize to 90px, the span actually gets the font-size and font-weight set in the cssText property.
var span = document.querySelector("span");
span.style.fontSize = "90px";
span.style.cssText = "font-size:20px; font-weight:bold;";
<span>Some Text</span>
So either set each style property separately or set cssText first
Those two lines:
element.style.position = "fixed";
element.style.cssText = "top: 0; left: 0; background-color: " + element_bg_color + "; margin: 0; padding: .3em .6em; border-radius: 0 0 5px 0; color: " + element_font_color + "; pointer-events: " + element_pointer_events + ";";
Switch them.
Explanation:
element.style.position = "fixed";
This line adds position: fixed; to the style attribute of element. So your element looks like
<div id="test_div" style="position: fixed;"></div>
And then:
element.style.cssText = "top: 0; left: 0; background-color: " + element_bg_color + "; margin: 0; padding: .3em .6em; border-radius: 0 0 5px 0; color: " + element_font_color + "; pointer-events: " + element_pointer_events + ";";
This lines replaces the entire style attribute with whatever comes after the = sign.
So your element looks like this:
<div id="test_div" style="/* lots of things here, but NOT "position: fixed;" anymore */"></div>
And here's a fiddle because why not?: http://jsfiddle.net/kxqgr913/
Here is a short snippet jsfiddle.net/nffubk14/ to explain how to add a div, append a text node to it and give it a style, hope this helps you understand what's going on. For more reading on this subject learn about DOM (Document Object Model)

Categories