How to display the arrays in a Textarea element - javascript

I want to show the very hard array and hard array in the textarea. Right now, it shows under the textarea as I don't know how to show it in the textarea. The user gives the input and the server response with the hard and very hard sentences from the user input. The hard sentences have a yellow background and the very hard have red background. For now, only the hard and very hard sentences with yellow and red background respectively is shown below the textarea and not the whole thing but I think it isn't intuitive as the user would have to go and search for the sentences in the textarea again as where the sentence exactly lies. So I want the whole thing to be shown in the textarea itself with the hard and very hard sentences highlighted in yellow and red background.
Right now my code looks something like this:
state={
hardArray: [],
vhardArray: []
}
performHard = async () => {
const { enteredText } = this.state;
const body = { text: enteredText };
const stringifiedBody = JSON.stringify(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: stringifiedBody
};
const url = "api/same";
try {
const response = await fetch(url, options);
const result = await response.json();
this.setState(prevState => ({
hardArray: [...prevState.hardArray, ...result.hard]
}));
} catch (error) {
console.error("error");
}
};
performVHard = async () => {
const { enteredText } = this.state;
const body = { text: enteredText };
const stringifiedBody = JSON.stringify(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: stringifiedBody
};
const url ="api/same";
try {
const response = await fetch(url, options);
const result = await response.json();
this.setState(prevState => ({
vhardArray: [...prevState.vhardArray, ...result.very_hard]
}));
} catch (error) {
console.error("error");
}
};
performAnalysis = () => {
this.performHard();
this.performVHard();
};
<textarea
name="enteredText"
className="textareaclass"
placeholder="Enter your text here"
onChange={this.handleChange}
value={enteredText}
></textarea>
<Button
className="rectangle-3"
onClick={this.performAnalysis}>Analyse
</Button>
<div>
{this.state.hardArray.map((word, i) => (
<span className="hardColor">{word}</span>
))}
{this.state.vhardArray.map((word, i) => (
<span className="vhardColor">{word}</span>
))}
</div>
edit: this is how I receive the respond from the server
{
"hard": [
"It's the possibility of having a dream come true that makes life interesting",
"I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation.",
]
"very_hard": [
“He stepped down, trying not to look long at her, as if she were the sun, yet he saw her, like the sun, even
without looking.”
]
}
I want to show all the text in the same textarea where the user wrote his content instead of showing anywhere else in the browser as it will make everything look ugly.

You can only give a textarea one background color, but you can make it transparent and put things behind it to add some color, yea this is a total hack and you will need to fiddle with the sizes and the blank lines to move the text down a bit - I will leave that exercise to you.
This also does not show how to get your values into the textarea but that is simple JavaScript/react code perhaps.
I altered this with some functions to illustrate where you MIGHT simply add/remove blank lines in the textarea to match the height of the background color - would probably have to adjust that background to match when this overflows the size, OR you might adjust the background to make it smaller for the colors.
I will leave it to you to determine which is the better option, I used "|" and "||" as the line/section separators as once it is in the textarea and edited you will need something like that.
All I have time for right now to enhance this but should give a starting point for this somewhat edge case without a clear standard solution.
.message {
width: 300px;
height: 150px;
display: block;
position: absolute;
}
textarea.format-custom,
.custom-underlay,
.underlay {
margin: 0;
box-sizing: border-box;
vertical-align: top;
display: block;
position: absolute;
border: lime solid 1px;
}
textarea.format-custom {
width: 100%;
height: 100%;
background: transparent;
resize: none;
display: block;
}
.underlay {
width: 100%;
height: 100%;
background: transparent;
display: block;
z-index: -1;
}
.custom-underlay {
width: 100%;
height: 50%;
margin: 0;
box-sizing: border-box;
vertical-align: top;
}
.custom-underlay.top {
background-color: #FFDDDD;
top: 0;
left: 0;
}
.custom-underlay.bottom {
background-color: #DDDDFF;
top: 50%;
left: 0;
}
<div class="message">
<label for="msg">Your message:</label>
<textarea id="msg" name="user_message" class="format-custom">howdy, I am here
bottom of here</textarea>
<div class="underlay">
<div class="custom-underlay top"></div>
<div class="custom-underlay bottom"></div>
</div>
</div>
Alternate idea from question, put text on the div's behind:
'use strict';
// borrowed code from https://stackoverflow.com/a/17590149/125981
// makeClass - By Hubert Kauker (MIT Licensed)
// original by John Resig (MIT Licensed).
var makeClass = (function(Void) {
return function() {
var constructor = function() {
var init = constructor.prototype.init,
hasInitMethod = (typeof init == "function"),
instance;
if (this instanceof constructor) {
if (hasInitMethod) init.apply(this, arguments);
} else {
Void.prototype = constructor.prototype;
instance = new Void();
if (hasInitMethod) init.apply(instance, arguments);
return instance;
}
};
return constructor;
};
})(function() {});
//make a class MyApp using above
var MyApp = makeClass();
// create MyApp functional part using the init:
MyApp.prototype.init = function(myItem, showmeClass = "showme", separator = "|", groupSeparator = "||") {
let eventChangeName = "change";
let textElement = document.getElementById(myItem);
let showme = textElement.closest(".container").getElementsByClassName(showmeClass)[0];
let lineSep = "|\n";
let myData = {
hard: [],
very_hard: []
};
this.sentData = {
hard: [],
very_hard: []
};
//so we can tell the lines
function getStyle(elId, styleProp) {
var x = document.getElementById(elId);
let y = {};
if (x.currentStyle) {
y = x.currentStyle[styleProp];
} else if (window.getComputedStyle) {
y = document.defaultView.getComputedStyle(x, null).getPropertyValue(styleProp);
}
return y;
}
function getTextareaThings(myTextarea) {
let taComputedStyles = window.getComputedStyle(myTextarea);
return {
height: myTextarea.style.height,
rows: myTextarea.rows,
clientHeight: myTextarea.clientHeight,
lineHeight: taComputedStyles.getPropertyValue('line-height'),
font: taComputedStyles.getPropertyValue('font-size')
};
}
function getLinesInString(myString) {
/* new line things: /r old Mac, /cr/lf some, /n some
all the "new line": regex: /\r\n|\n|\r/gm
above reduced regex: g and m are for global and multiline flags */
let nl = /[\r\n]+/gm;
let lines = [];
lines = myString.split(nl);
return lines;
}
function splitGroupString(myString, separator) {
let strings = [];
strings = myString.split(separator);
return strings;
}
function getGroupsInString(myString) {
return splitGroupString(myString, groupSeparator);
}
function getGroupItemsInString(myString) {
return splitGroupString(myString, separator);
}
function getCurrentValue() {
return textElement.value;
}
function addNewLines(text, count) {
let newLine = "\n";
return text + newLine.repeat(count);
}
// make stuff obvious
function onFocusTextareaValue(event) {
showForDebug(event);
}
function onChangeTextareaValue(event) {
if (event.type == eventChangeName) {
event.stopPropagation();
event.stopImmediatePropagation();
}
showForDebug(event);
}
function showForDebug(event) {
let what = "Event: " + event.type;
let b = "<br />";
let tat = getTextareaThings(event.target);
let v = getCurrentValue().replace(what, "");
showme.innerHTML = what + b + ": lines:" + getLinesInString(v).length + b + v;
}
function getStringLineCount(arr) {
arr.length;
}
function getGroupItems() {
let groups = getGroupsInString(getCurrentValue());
let groupItems = {
count: groups.length, // how many groups, two in the definition (top/bottom)
groups: []
};
groups.forEach(function(group, index, groupsArr) {
let items = getGroupItemsInString(group);
// determine how to define "group name", I use a string and the index here
let gname = "group" + index;
let g = {
itemCount: items.length // number in this group
};
g[gname] = {
items: []
};
items.forEach(function(item, itemindex, itemsArr) {
let itemName = "item" + itemindex;
let itemobj = {};
itemobj[itemName] = {
items: item
};
g[gname].items.push(itemobj);
});
groupItems.groups.push(g);
});
return groupItems;
}
// setup events
textElement.addEventListener(eventChangeName, onChangeTextareaValue, false);
textElement.addEventListener("focus", onFocusTextareaValue, false);
this.getGeometry = function() {
let geometry = {};
let element = textElement;
let rect = element.getBoundingClientRect();
geometry.top = rect.top;
geometry.right = rect.right;
geometry.bottom = rect.bottom;
geometry.left = rect.left;
geometry.offsetHeight = element.offsetHeight;
geometry.rows = element.rows;
geometry.clientHeight = element.clientHeight;
geometry.fontSize = this.getStyleProperty("font-size");
geometry.lineCount = this.getLines().length;
geometry.lineHeight = this.getLineHeight();
geometry.height = geometry.bottom - geometry.top;
geometry.width = geometry.right - geometry.left;
console.log("Geometry:",geometry);
};
this.getMetrics = function() {
let fSize = this.getStyleProperty("font-size");
let lineCount = this.getLines().length;
let lineHeight = this.getLineHeight();
let yh = lineHeight / lineCount;
let yfhPixel = parseInt(fSize, 10);
let yLineY = yh * yfhPixel;
console.log("LH:", lineHeight, "font:", fSize, "Lines:", lineCount, "lineHeight:", lineHeight, "yh:", yh, "yfPixel:", yfhPixel, "yLineY:", yLineY);
};
this.getStyleProperty = function(propertyName) {
return getStyle(textElement.id, propertyName)
};
// public functions and objects
this.getLines = function() {
return getLinesInString(getCurrentValue());
};
this.getGroups = function() {
return getGroupsInString(getCurrentValue());
};
this.setText = function(content) {
if (!content) {
content = this.sentData;
}
let hard = content.hard.join(lineSep);
let veryHard = content.very_hard.join(lineSep);
this.textElement.value = hard.concat("|" + lineSep, veryHard);
};
this.getLineHeight = function(element) {
if (!element) {
element = textElement;
}
let temp = document.createElement(element.nodeName);
temp.setAttribute("style", "margin:0px;padding:0px;font-family:" + element.style.fontFamily + ";font-size:" + element.style.fontSize);
temp.innerHTML = "test";
temp = element.parentNode.appendChild(temp);
let lineHeight = temp.clientHeight;
temp.parentNode.removeChild(temp);
return lineHeight;
};
this.getGroupItems = function() {
return getGroupItems();
};
this.textElement = textElement;
this.showme = showme;
};
let sentData = {
hard: [
"It's the possibility of having a dream come true that makes life interesting",
"I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation."
],
very_hard: ["He stepped down, trying not to look long at her, as if she were the sun, yet he saw her, like the sun, even without looking."]
};
// create instances and use our app, pass the id
var containerApp = MyApp("textThing"); //default last three parameters
containerApp.sentData = sentData;
containerApp.setText();
let groups = containerApp.getGroups();
let groupItems = containerApp.getGroupItems();
containerApp.getMetrics();
containerApp.getGeometry();
// create instances and use our app, pass the id
var containerApp2 = MyApp("msgTwo", "showme", "|", "||");
//console.log("Second One Lines:", containerApp2.getLines().length);
//containerApp2.getMetrics();
//containerApp2.getGeometry();
.page-container {
display: flex;
/* center and stack the containers*/
justify-content: center;
flex-direction: column;
align-items: center;
font-size: 62.5%;
}
.container {
border: solid 1px black;
}
.container-block {
border: 2px dashed #AAAAAA;
}
.container-items {
width: 500px;
position: relative;
}
.container-items .format-custom,
.container-items label {
width: 100%;
}
.container-items .format-custom {
height: 10em
}
.message-hr {
border: 1px solid blue;
background-color: blue;
height: 0.05em;
width: 450px;
align-items: center;
margin: 0.5em;
}
.showme {
border: dotted 2px dodgerblue;
background-color: #EEEEEE;
padding: 1em;
}
textarea.format-custom,
.custom-underlay,
.underlay {
margin: 0;
box-sizing: border-box;
vertical-align: top;
display: block;
border: lime solid 1px;
}
textarea.format-custom {
width: 100%;
height: 3em;
background: transparent;
resize: none;
border: red solid 1px;
padding: 0.5em;
}
.underlay {
border: 1px solid #fff;
width: 100%;
height: 100%;
background: transparent;
top: 1em;
left: 0;
display: block;
z-index: -1;
position: absolute;
}
.custom-underlay {
width: 100%;
height: 50%;
margin: 0;
box-sizing: border-box;
vertical-align: top;
position: absolute;
}
.custom-underlay.top {
background-color: #FFFF00;
top: 0;
left: 0;
}
.custom-underlay.bottom {
background-color: #FFAAAA;
top: 50%;
left: 0;
}
<div class="page-container">
<div class="container">
<div class="container-block">
<div class="container-items">
<label for="textThing">Your message:</label>
<textarea id="textThing" name="textThing" class="format-custom">howdy, I am here|another one | cheese burgers
fries and a drink
||
bottom of here| bottom second</textarea>
<div class="underlay">
<div class="custom-underlay top"></div>
<div class="custom-underlay bottom"></div>
</div>
</div>
</div>
<div class="showme">xxxone</div>
</div>
<div class="container">
<div class="container-block">
<div class="message-hr container-items">
</div>
</div>
</div>
<div class="container">
<div class="container-block">
<div class="container-items">
<label for="msgTwo">Second message:</label>
<textarea id="msgTwo" name="msgTwo" class="format-custom">Not the same|Nxxt one
||
bottom of next</textarea>
<div class="underlay">
<div class="custom-underlay top"></div>
<div class="custom-underlay bottom"></div>
</div>
</div>
</div>
<div class="showme">xxxtwo</div>
</div>
</div>

its not with color but in this code you can set a lable like this (hard: veryhard :)
state = {
value: "",
hard: [],
veryHard: []
};
handleChange = ({ target }) => {
const { value, name } = target;
console.log(value, name);
this.setState({ value });
};
performAnalysis = () => {
let hard = [
//this state ,get from performHard function (you should set state it )
"It's the possibility of having a dream come true that makes life interesting",
"I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation."
];
let veryHard = [
//this state ,get from performVHard function (you should set state it )
"“He stepped down, trying not to look long at her, as if she were the sun, yet he saw her, like the sun, even without looking.“"
];
this.setState({ hard, veryHard });
// you shoud setState like this in these 2 function
// this.performHard();
// this.performVHard();
};
render() {
return (
<div className="App">
<header className="App-header">
<textarea
value={
this.state.value.length
? this.state.value
: " Hard : " +
" " +
this.state.hard +
" " +
"very Hard : " +
this.state.veryHard
}
onChange={this.handleChange}
/>
<button onClick={this.performAnalysis}>analise</button>
</header>
</div>
);
}
its not exactly that you want,but you can get help from this code

Related

Why is my removeEventListener not working? Looked through other posts, can't find an answer

This is a function where you click on a character and it heals them by the amount specified in the switch. I need the listener to trigger only one time per function call but the removal doesn't seem to be doing anything. As in you can just keep clicking and it keeps going through the switch. I've tested and ensured that the function itself is only being called once so I have no idea what's going on here. Tried removing the listener like document.removeEventListener('click', addAllyTargets) as well, no change. Angels_Grace_Part2() is purely imagery/text and has nothing to do with the listener.
var ally_targets = []
//make the list of targets
function makeAllyTargets() {
let maketargets = document.getElementsByClassName('ally_img')
for (let i = 0; i < maketargets.length; i++) {
ally_targets.push(maketargets[i])
};
};
var amt_healed;
function Angels_Grace() { //moderate healing spell on one ally
makeAllyTargets();
for (let i = 0; i < ally_targets.length; i++) {
//add the listener to each target
ally_targets[i].addEventListener('click', function addAllyTargets() {
//amt healed is 55% of the target's max.
const selected_ally = ally_targets.indexOf(this);
switch (selected_ally) {
case 0: //knight
amt_healed = 303;
//ensure it doesn't go over max
if (warrior_hp.value + amt_healed > 550) {
Angels_Grace_Part2()
warrior_hp.value = 550;
} else {
Angels_Grace_Part2()
warrior_hp.value += amt_healed;
};
//remove the listener
ally_targets[i].removeEventListener('click', addAllyTargets)
break;
//the other cases follow the same logic
default:
console.log("heal switch - shits fucked")
break;
};
});
};
};
I went overboard in my answer.
There were a lot of things going on in your code that, in my opinion, could have been made simpler.
And so rather than go into a deep explanation as to why, I offer this for your consideration.
function AngelsGrace(event) {
//amt healed is 55% of the target's max.
let ds = event.target.dataset;
let hp = +ds.hp;
let maxHp = +ds.maxHp;
let healingRate = +ds.healingRate;
let amtHealed = maxHp * healingRate;
let excessiveHealing = hp + amtHealed > maxHp;
if (excessiveHealing)
amtHealed = maxHp - hp;
ds.hp = hp + amtHealed;
if (amtHealed) {
console.log(`${ds.class} hp was ${hp}. Healed for ${amtHealed}, now ${ds.hp}/${maxHp}`);
} else {
console.log(`${ds.class} is at Full Health`);
}
console.log("casting Angels_Grace_Part2()");
event.target.nextElementSibling.classList.toggle("angels-grace");
}
function CastAngelsGrace() { //moderate healing spell on one ally
var oneTimeOnly = {
once: true
};
let htmlCollection = document.getElementsByClassName('ally_img');
for (const tgt of htmlCollection) {
tgt.addEventListener('click', AngelsGrace, oneTimeOnly)
tgt.nextElementSibling.classList.toggle("angels-grace");
};
}
.character {
display: inline-block;
position: relative;
height: 100px;
width: 100px;
border: 1px solid grey;
margin: 10px;
}
.ally_img {
height: 70px;
width: 70px;
}
.spell-container {
position: absolute;
top: 0;
right: 0;
}
.spell-container:after {
opacity: 0;
}
.spell-container.angels-grace:after {
content: "AG";
opacity: 1;
}
<div class="character">
<image src="https://picsum.photos/id/718/200" class="ally_img"
data-class="knight"
data-healing-class="warrior"
data-healing-rate="0.55"
data-max-hp="1000"
data-hp="200" />
<span class="spell-container"></span>
</div>
<div class="character">
<image src="https://picsum.photos/id/237/200" class="ally_img"
data-class="rogue"
data-healing-class="sneaky"
data-healing-rate="0.35"
data-max-hp="500"
data-hp="100" />
<span class="spell-container"></span>
</div>
<button onclick="CastAngelsGrace()">Cast Angel Grace</button>

How to inject an id attribute to HTML with its value coming from a for loop using JavaScript?

const renderProgress = () => {
let qIndex = 0;
const lastQuestion = 20
const queryAllProgress = document.getElementsByClassName("query__all-progress");
const queryAllProgressId = document.createAttribute("id");
for (qIndex; qIndex <= lastQuestion; qIndex++) {
queryAllProgressId.value += qIndex;
queryAllProgress.setAttributeNode(queryAllProgressId);
}
};
.query__all-progress {
width: 0.9rem;
height: 0.9rem;
margin: 0 0.03rem;
border: 1px solid grey;
display: inline-block;
border-radius: 40%;
}
<div class="query__all-progress"></div>
As you can see, I am trying to get the div element by the class name query__all-progress 'cause I need to give it an attribute of id="". And, the value of the id should be from the for loop, which is the qIndex. I tried to do this but it doesn't work. Please help.
I'm just trying to refactor this code guys. Please help:
const renderProgress = () => {
const lastQuestion = 20;
let qIndex = 0;
const queryProgress = document.getElementById("query__progress");
for (qIndex; qIndex <= lastQuestion; qIndex++) {
queryProgress.innerHTML += `<div class='query__all-progress' id="${qIndex}"></div>`;
}
};
You can try to solve it like this:
In your .html file create a element to serve as a container
<div id="container"></div>
I modified your function a bit like this:
const renderProgress = () => {
// COUNTING VARIABLES
let qIndex = 0;
const lastQuestion = 20
// TO STORE EACH ELEMENT
let querys = '';
// PROCESS
for (qIndex; qIndex <= lastQuestion; qIndex++) {
querys += '<div class="query__all-progress" id="'+ qIndex +'"></div>';
}
// INJECTING GENERATED ELEMENTS TO CONTAINER
document.getElementById('container').insertAdjacentHTML('afterbegin', querys);
}
renderProgress();
.css stays the same
.query__all-progress {
width: 0.9rem;
height: 0.9rem;
margin: 0 0.03rem;
border: 1px solid grey;
display: inline-block;
border-radius: 40%;
}
You should have something like this:
And in the inspector:
I hope it helps you.

Why does left-padding becomes part of span?

I am creating chinese checkers, I use span tag to create circles. Added only left padding to the top corner. I have two questions:
1) Why rows seem to have distance between them, but not columns.
2) To fix 1) I added padding-left, but instead of adding distance the padding became part of the circle, why?
Here's the link how it looks:
Here's part of code:
.player0{
height: 40px;
width: 40px;
padding-right: 5px;
background-color: transparent;
border-radius: 50%;
display: inline-block;
}
divs += "<span class='player"+fullBoardArr[fullArrIter]+" 'onclick='send()'></span>"
divs += "<div class='clear_float'> </div>" //for separation of rows
As I said in comments, you need to use margin instead of padding.
I would not use "clear_float" (I assume this is about the float CSS property). Instead wrap elements that belong in the same row, in a separate div element.
From the image you included, it seems that you have a problem in aligning the cells. You can use many ways to solve this, but as your board is symmetric horizontally (ignoring the colors), you can just use text-align: center.
I had some fun in creating JavaScript logic for the board itself. You may find some aspects interesting to reuse:
class Cell {
constructor(rowId, colId) {
this._value = 0;
this.rowId = rowId;
this.colId = colId;
this.elem = document.createElement("span");
this.elem.className = "cell";
this.selected = false;
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
this.elem.style.backgroundColor = ["", "grey", "blue", "red"][value];
}
toggleSelected() {
this.selected = !this.selected;
this.elem.classList.toggle("selected", this.selected);
}
}
class Board {
constructor() {
this._container = document.createElement("div");
this._container.className = "board";
this.elemMap = new Map;
this.grid = [[0,0,0,0,2,0,0,0,0,0,0,0,0],
[0,0,0,0,2,2,0,0,0,0,0,0,0],
[0,0,0,0,2,2,2,0,0,0,0,0,0],
[0,0,0,0,2,2,2,2,0,0,0,0,0],
[3,3,3,3,1,1,1,1,1,4,4,4,4],
[0,3,3,3,1,1,1,1,1,1,4,4,4],
[0,0,3,3,1,1,1,1,1,1,1,4,4],
[0,0,0,3,1,1,1,1,1,1,1,1,4],
[0,0,0,0,1,1,1,1,1,1,1,1,1]];
// create the data structure for the game and produce the corresponding DOM
this.grid.forEach((row, rowId) => {
let div = document.createElement("div");
row.forEach((value, colId) => {
if (!value--) return;
let cell = row[colId] = new Cell(rowId, colId);
cell.value = value;
div.appendChild(cell.elem);
this.elemMap.set(cell.elem, cell);
});
this._container.appendChild(div);
});
}
set container(elem) {
elem.appendChild(this._container);
}
getEventCell(e) {
return this.elemMap.get(e.target);
}
set selected(cell) {
if (this._selected) {
this._selected.toggleSelected();
this._selected = null;
}
if (!cell) return;
cell.toggleSelected();
this._selected = cell;
}
get selected() {
return this._selected;
}
move(cellFrom, cellTo) {
// TODO: Implement the real move rules here
if (!cellFrom.value) return; // must move a piece
if (cellTo.value) return; // capturing not allowed
cellTo.value = cellFrom.value;
cellFrom.value = 0;
board.selected = null;
}
}
let container = document.querySelector("#container");
let board = new Board();
board.container = container;
container.addEventListener("click", e => {
let cell = board.getEventCell(e);
if (!cell) return; // click was not on a cell
if (!board.selected || cell.value) {
board.selected = cell;
} else {
board.move(board.selected, cell);
}
});
.board {
text-align: center;
margin-left: auto; margin-right: auto;
}
.cell {
height: 15px;
width: 15px;
margin: 0px 2px;
border: 1px solid;
border-radius: 50%;
display: inline-block;
}
.selected {
border-color: orange;
}
<div id="container"></div>
You can click to select a piece and then click again on an empty spot to move it there.
Use margin instead of padding:
.player0{
height: 40px;
width: 40px;
margin-right: 5px;
background-color: transparent;
border-radius: 50%;
display: inline-block;
}
As an easy-to-remember quick reference, margin changes the position starting from outside the element border, padding from the inside

How to create a ludo board dynamically using Javascript

I am making a board game ludo. I contains four players. The board of ludo looks something like
I have managed to create some part of it dynamically using Javascript.
const qs = str => document.querySelector(str);
const qsa = str => document.querySelectorAll(str);
const ce = (str, props) => {
let elm = document.createElement(str);
if(props){
for(let k in props){
elm[k] = props[k]
}
}
return elm;
}
let main = qs('#main');
function createDiv(type, color){
let div = ce('div');
div.style.backgroundColor = color;
div.style.display = type;
}
let game = Array(52).fill(0);
function createPlayer(color,angle){
let div = ce('div', {className: 'player-cont'})
let table = ce('table');
div.style.transform = `rotate(${angle}deg)`
function createRow(len,colorsSet){
const tr = ce('tr',{className:'tile-row'});
[...Array(len)].forEach((x,i) =>{
let elm = ce('td', {className:'tile'});
if(colorsSet.has(i)){
elm.style.backgroundColor = color;
}
tr.appendChild(elm)
})
return tr;
}
function createBase(){
const base = ce('table', {className: 'base'});
[...Array(2)].forEach(x => {
let row = ce('tr', {className: 'base-row'});
[...Array(2)].forEach(a => {
let td = ce('td', {className: 'base-tile'})
row.appendChild(td);
})
base.appendChild(row)
})
return base;
}
let colorSets = [
new Set(),
new Set([0,1,2,3,4]),
new Set([4])
]
colorSets.forEach(x => {
table.appendChild(createRow(6, x))
})
div.appendChild(table);
div.appendChild(createBase());
return div;
}
let colors = ['red','blue','green','pink'];
colors.forEach((x, i) => {
main.appendChild(createPlayer(x, (i * 90) - 180));
})
.tile {
height: 50px;
width: 50px;
background: orange;
border: 1px solid black;
display: inline-block;
}
table.base td {
height: 50px;
background: orange;
width: 50px;
border-radius: 100px;
}
.player-cont {
background: aliceblue;
width: fit-content;
}
<div id = "main"></div>
But now I am not sure how will I finish this up. You can ignore two things in the above image:
The big border of the of big square of different colors. I just need a single color throughout.
You can ignore the colorful triangles in center. I just need a one color box instead of that.

I want to display the ID of clicked Item in a class and delete it if clicked again

I am trying to make an interface where you can select tickets and buy them, I want that when I click on a seat it displays like "You are currently buying the next tickets + (The tickets chosen by the user)".
This is my code so far:
var seatsUnclicked = document.getElementsByClassName("seat-unique");
var seatsClicked = document.getElementsByClassName("seatClicked");
var images = document.getElementsByTagName("IMG");
var seatsOutput = document.getElementsById("seatsOutput");
var ticketsData = 0
for (let i = 0; i < seatsUnclicked.length; i++) {
seatsUnclicked[i].onmouseover = function() {
this.src = "chairclicked.svg";
this.onmouseout = function() {
this.src = "chair.svg"
}
if ($(this).hasClass('seatClicked')) {
this.src = "chairclicked.svg";
this.onmouseout = function() {
this.src = "chairclicked.svg"
}
}
}
seatsUnclicked[i].onclick = function() {
this.classList.add("new")
if ($(this).hasClass('seatClicked')) {
this.classList.remove("seatClicked")
this.classList.remove("new")
this.src = "chair.svg";
this.onmouseout = function() {
this.src = "chair.svg"
}
ticketsData = ticketsData - /* "the id of this element in a string" */
}
if ($(this).hasClass('new')) {
this.src = "chairclicked.svg";
this.classList.add("seatClicked")
this.classList.remove("new")
this.onmouseout = function() {
this.src = "chairclicked.svg"
}
ticketsData = ticketsData + /* "the ID of this element in a string" */
}
seatsOutput.innerHTML = "THE TICKETS YOU HAVE CHOSEN ARE" + string(ticketsData)
}
}
<div class="seats-row-A">
<img id="A1" class="seat-unique " src="http://via.placeholder.com/100x100?text=A1">
<img id="A2" class="seat-unique " src="http://via.placeholder.com/100x100?text=A2">
<img id="A3" class="seat-unique " src="http://via.placeholder.com/100x100?text=A3">
<img id="A4" class="seat-unique " src="http://via.placeholder.com/100x100?text=A4">
<img id="A5" class="seat-unique" src="http://via.placeholder.com/100x100?text=A5">
<img id="A6" class="seat-unique " src="http://via.placeholder.com/100x100?text=A6">
<img id="A7" class="seat-unique " src="http://via.placeholder.com/100x100?text=A7">
</div>
<h2 id="seatsOutput">Chosen Tickets:</h2>
jQuery
The only jQuery statement in OP code is: $(this).hasClass('seatClicked').
The plain JavaScript equivalent is: this.classList.contains('seatClicked').
Question
I couldn't follow the OP code because there was only a class, an id, and img tags that match the JavaScript, but it's not that clear because of the *.svg files (not provided.) Also, there's a curly bracket } missing (I think it belongs to the for loop, but I'm not wasting time on debugging typos.)
The Demo was built in mind with what the question and comments had mentioned:
"...I want that when I click on a seat it displays like "You are currently buying..."
Highlight icon when hovered over.
Reveal icon's id when hovered on.
All hover behavior is done with CSS: :hover, ::before, ::after, content: attr(id), content: '\a0\1f4ba'. Using JavaScript for behavior CSS can do will result in more CPU cycles. CSS will use GPU of your graphics card instead of the CPU.
Testing
The seats are dynamically generated with id="A* by entering a number in the input and clicking the View button. For each additional click of the button a new batch of seats are appended and have ids that correspond to it's group:
input: 55 and first click A0-A55,
input: 12 and second click B0-B12,
input: 222 and third click C0-C222
...
Last group is J
References
The Demo is basically a <form>. HTMLFormControlsCollection API is used to set/get form controls and values.
Reference the tag
const ui = document.forms.tickets;
This is a collection of all form controls in form#tickets
const t = ui.elements;
Now all form controls are now accessible by prefixing a form control's #id or [name] with the HTMLFormControlsCollection Object.
<textarea name='textA'></textarea>
Without HFCC API
var ta = document.querySelector('[name=textA]');
With HFCC API
var ta = t.textA;
The links are collected by Links Collection.
document.links
DocumentFragment is used to insert a huge amount of dynamic HTML in one shot efficiently and quickly.
document.createDocumentFragment();
Various array methods were used:
Array.from()
map()
fill()
indexOf()
Demo
const ui = document.forms.tickets;
const t = ui.elements;
const seats = document.getElementById('seats');
t.btn.addEventListener('click', seatsAvailable);
seats.addEventListener('click', function(e) {
let picked = [];
pickSeat(e, picked);
}, false);
function pickSeat(e, picked) {
const display = t.display;
if (e.target.tagName === "A") {
e.target.classList.toggle('picked');
picked = Array.from(document.querySelectorAll('.picked'));
}
picked = picked.map(function(seat, index, picked) {
return seat.id;
});
display.value = "";
display.value = picked;
}
function seatsAvailable(e) {
const qty = this.previousElementSibling;
const last = document.links[document.links.length - 1].id;
console.log(last);
const limit = parseInt(qty.value, 10) + 1;
const spots = new Array(limit);
spots.fill(0, 0, limit);
return generateSeats(spots, last);
}
function generateSeats(spots, last) {
if (last.charAt(0) === "J") {
t.display.textContent += "Last row available";
return false;
}
const rowID = ['x', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
let row = rowID.indexOf(last.charAt(0)) + 1;
const frag = document.createDocumentFragment();
const avSeats = spots.map(function(A, idx) {
const seat = document.createElement('a');
seat.id = rowID[row] + idx;
seat.href = "#/";
frag.appendChild(seat);
return seat;
});
seats.appendChild(frag);
if (document.links[0].id === 'x') {
const x = document.getElementById('x');
x.parentElement.removeChild(x);
}
if (document.links.length > 114) {
const ext = (Math.round(document.links.length / 114)*600)+600;
seats.style.maxHeight = ext+'px';
}
return avSeats;
}
html,
body {
width: 100%;
height: 10%;
font: 400 16px/1.3 Consolas;
}
#seats {
display: flex;
flex-flow: column wrap;
max-height: 600px;
width: auto;
border: 3px ridge grey;
}
.box {
display: table
}
input,
button,
label {
font: inherit
}
#qty {
text-align: right
}
#display {
display: table-cell;
}
.tag {
overflow-x: scroll;
overflow-y: hidden;
display: block;
width: 400px;
line-height: 1.3
}
a,
a:link,
a:visited {
display: inline-block;
margin: 0;
text-align: center;
text-decoration: none;
transition: all 500ms ease;
}
a:hover,
a:active {
background: rgba(0, 0, 0, 0);
color: #2468ac;
box-shadow: 0 0 0 3px #2468ac;
}
a::before {
content: attr(id);
color: transparent;
}
a:hover::before {
color: #2468ac;
}
a.picked::before {
color: #000;
}
a::after {
content: '\a0\1f4ba';
font-size: 1.5rem;
}
#x {
pointer-events: none
}
.as-console-wrapper {
width: 30%;
margin-left: 70%
}
<form id='tickets'>
<fieldset class='box'>
<legend>Available Seats</legend>
<fieldset class='box'>
<input id='qty' type='number' min='0' max='50' value='1'> <button id='btn' type='button'>View</button>
<label class='tag'>Current ticket purchases to seats:
<output id='display'></output>
</label>
</fieldset>
<section id='seats'>
<a id='x' href='#/'></a>
</section>
</fieldset>
</form>

Categories

Resources