I am creating an interactive website where the user is able to click anywhere on the page and each paragraph will show up one by one. At the moment the paragraphs are displaying to the right of the click but i want the paragraph to be centred on the mouse position. Is there a way to do this?
Is there also a way to restrict the paragraphs from going off the screen as this creates a landscape scroll which i don't want, Any help would be appreciated.
const texts = [
"Paragraph: 1",
"Paragraph: 2",
"Paragraph: 3",
"Paragraph: 4",
"Paragraph: 5",
"Paragraph: 6",
"Paragraph: 7",
"Paragraph: 8",
"Paragraph: 9",
"Paragraph: 10"
];
const classes = [
"red gray-bg font-1",
"blue font-2",
"red",
"blue",
"red",
"blue",
"red",
"blue",
"red",
"blue",
];
// Keeps Track of Current Text
let currentParaIdx = 0;
document.addEventListener("click", (e) => {
// Stop once All Texts are Displayed
if (currentParaIdx === texts.length) return;
const { clientX, clientY } = e; //get the click position
//create the div
const div = document.createElement("div");
//set its text
div.innerText = texts[currentParaIdx];
//set its position and position style
div.classList=classes[currentParaIdx];
div.style.position = "absolute";
div.style.left = clientX "px";
div.style.top = clientY "px";
currentParaIdx ;
document.body.append(div); //add div to the page
});
This is the code I have so far
CodePudding user response:
You could use offsetWidth to center the element at your mouse position. Like clientX - half of your element's width.
And the other thing, I think this is what you need: overflow
CodePudding user response:
Notes
While there isn't anything wrong with your code, there are a few things that could be changed that should make things a little more optimized and easier to manage your code going forward.
The first is your two arrays for the text and classes. Creating one array of objects would allow you to only have to work with one array, and you can pull whichever key/property you need depending on what you are doing.
Another thing is using template literals instead of string concatenation when setting some of your values. This is a small thing, but typically a good idea to do.
Solution
Essentially, if you want to center an element on your mouse/cursor position, you need to move it by half of the elements width
and height
. In this situation, we are working with an element that is being dynamically created and so it initially will not have a width
or height
. We can add it to the document first, pull those values and then adjust the top
and left
properties accordingly. And because JavaScript is processed synchronously, this won't be noticeable to the user.
const paragraphs = [
{text: `Paragraph: 1`, class: "red gray-bg font-1"},
{text: `Paragraph: 2`, class: "blue font-2"},
{text: `Paragraph: 3`, class: "red"},
{text: `Paragraph: 4`, class: "blue"},
{text: `Paragraph: 5<br>Different sized paragraph`, class: "red"},
{text: `Paragraph: 6<br><br><br>Another different sized paragraph`, class: "blue"},
{text: `Paragraph: 7 | This is a wider paragraph`, class: "red"},
{text: `Paragraph: 8 | This is also wider but max-width prevents this paragraph from becoming too wide`, class: "blue"},
{text: `Paragraph: 9`, class: "red"},
{text: `Paragraph: 10`, class: "blue"}
]
let currentParaIdx = 0
document.body.addEventListener("click", e => {
if(currentParaIdx === paragraphs.length) return
const { clientX, clientY } = e,
div = document.createElement("div")
div.innerHTML = paragraphs[currentParaIdx].text
div.className = `${paragraphs[currentParaIdx].class} paragraph`
currentParaIdx
document.body.append(div)
// Once the div is added, we can get its computed size
// Then calculate if it will go off screen or not
let bodyW = parseInt(getComputedStyle(document.body).width),
bodyH = parseInt(getComputedStyle(document.body).height),
divW = parseInt(getComputedStyle(div).width),
divH = parseInt(getComputedStyle(div).height),
divX = (clientX - Math.round(divW / 2) < 0) ? 0 : (clientX Math.round(divW / 2) > bodyW) ? bodyW - (divW * 1.1) : clientX - Math.round(divW / 2),
divY = (clientY - Math.round(divH / 2) < 0) ? 0 : (clientY Math.round(divH / 2) > bodyH) ? bodyH - (divH * 1.1) : clientY - Math.round(divH / 2)
div.style.left = `${divX}px`
div.style.top = `${divY}px`
})
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.paragraph {
max-width: 500px;
position: absolute;
cursor: default;
}
.red { color: #F00; }
.blue { color: #00F; }
.gray-bg { background: #CCC; }
.font-1 { font-size: 1em; }
.font-2 { font-size: 1.1em; font-weight: bold; }
Additional Notes
I opted to use getComputedStyle()
here instead of something like getBoundingClientRect()
when getting things like the width
and height
because things like padding
on an element can make these values inaccurate. Not knowing the full context of how this will be used, I felt it was a safer choice.