Why the randomize function for absolute margin is not working? - javascript

I have a HTML and I'm trying to randomize each layer top margin attribute.
function randomize() {
let r;
let list = document.querySelectorAll("span");
for (let i = 0; i < list.length; i++) {
r = Math.floor(Math.random() * 50);
list.forEach((list) => {
style.top = `${r} + px`;
});
}
}
span {
position: absolute;
height: 20px;
width: 20px;
border-radius: 100%;
z-index: -2;
}
<div class="parallax">
<span class="layer" data-speed="-5">10110</span>
<span class="layer" data-speed="-5">0</span>
</div>
What am I getting wrong here? I can't find the proper solution.

A few things:
To use the top CSS property, the element needs to have a position defined. You can set that in the CSS (position: relative) or set it in the JS. (EDIT OP added CSS that they had already which set the position. Leaving this here for people that use top without setting an element's position)
You need to move the randomization into the forEach otherwise it will have the same value for all span elements
You need to attach the style prop to the element you are passing in the forEach, e.g., list.style.styleProp
function randomize() {
let r;
let list = document.querySelectorAll("span");
for (let i = 0; i < list.length; i++) {
list.forEach((list) => {
r = Math.floor(Math.random() * 50);
list.style.top = r + 'px';
});
}
}
randomize();
span {
position: absolute;
height: 20px;
width: 20px;
border-radius: 100%;
z-index: -2;
}
<div class="parallax">
<span class="layer" data-speed="-5">10110</span>
<span class="layer" data-speed="-5">0</span>
</div>

Because <span> is natively an inline element, and margin has no effect.
Change your <span>s to <div>s.

Related

How to add animate to the alphabets inside a react carousal?

I'm trying to do a carousal like in this CodePen using React Hooks.
My present result is in this Sandbox: Click..!
The problems I'm facing are:
I don't know how to make that CSS animation effect of the letters coming and forming the text and scattering back as in the CodePen example.
I want to include the description part also which is in the description={data.desc}. Do I have to make the split again or any easy method to split both title and description together. I lag knowledge here.
My code is as below:
import React from "react";
export default function SlideCard(props) {
const { id, idx, title } = props;
function mainText() {
return (
<div style={{ border: "2px solid gold" }}>
<h1>{title}</h1>
</div>
);
}
//console.log(id);
function scatter() {
return (
<div>
{title.split("").map((item, index) => {
const style = {
position: "absolute",
top: Math.floor(Math.random() * 200) + "px",
left: Math.floor(Math.random() * 400) + "px",
zIndex: "initial",
color: "#AAA",
overflow: "hidden",
transition: "left 2s, top 2s, color 2s"
};
return (
<div key={index}>
<div className="scatter" style={style}>
{item}
</div>
</div>
);
})}
</div>
);
}
return (
<div>
<div>{idx === id && mainText()}</div>
{idx !== id && scatter()}
</div>
);
}
He's maintaining two same copies of each page data. One with class .position-data and one with .mutable. .position-data is hidden and only used for getting coordinates to bring back .mutable letters together.
Here is the simplified version of the CodePen example:
$(document).ready(function() {
assemble();
});
function scatter() {
for (var i = 0; i < 3; i++) {
var randLeft = Math.floor(Math.random() * $(window).width());
var randTop = Math.floor(Math.random() * $(window).height());
//randomly position .mutable elements
$(".mutable > span:eq(" + i + ")").animate({
left: randLeft,
top: randTop,
color: "#0005"
}, 2000, "easeInBack");
}
}
function assemble() {
for (var i = 0; i < 3; i++) {
$(".mutable > span:eq(" + i + ")").animate({
//get center position from .position data marked elements
left: $(".position-data > span:eq(" + i + ")").offset().left + "px",
top: $(".position-data > span:eq(" + i + ")").offset().top + "px",
color: "#000"
}, 2000, 'easeOutBounce');
}
}
span {
font-size: 30px;
}
.position-data {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0.4;
color: green;
}
.mutable span {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="position-data">
<span>a</span>
<span>b</span>
<span>c</span>
</div>
<div class="mutable shadowed">
<span>a</span>
<span>b</span>
<span>c</span>
</div>
<button onclick="scatter()">Scatter</button>
<button onclick="assemble()">Assemble</button>
I want to position the actively displaying Text container to the centre of the carousal.
The actively displaying text container is marked with .mutable class which has following css:
.mutable {
position: absolute;
overflow: hidden;
display: inline-block;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
So they all have full size of the document body. Using javascript function arrangeCurrentPage() in codepen they are set with coordinates such that they look centered.
I don't know how to make that transition effect of the letters coming and forming the text and scattering back as in the CodePen example.
Refer to above simplified code. Use the buttons to see the effects separately. All letter elements are put in a div with class .mutable. All letters are position: absolute; so they are free to move anywhere. In javascript function scatter() I am simply assigning top and left properties to random coordinates. With animation effect they scatter smoothly. In codepen he is achieving the animation effect using css transition transition: left 2s, top 2s, color 2s;
To bring them back simply us css selector nth-child .position-data > span:eq(n) to get corresponding coordinates of the character. Refer javascript function assemble().
I want to include the description part also which is in the description={data.desc}. Do I have to make the split again or any easy method to split both title and description together. I lag knowledge here.
As title and description will be displayed separately with separate presentation styles. Code will be cleaner if you keep split versions of them separately. You can create utility methods like split(text), scramble(array), arrange(array)and use it for both like cramble(titleArray)and scramble(descArray).
If we use 'position:relative' on letters we don't have to maintain two copies of same data:
<!DOCTYPE html>
<head>
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script>
$(document).ready(function() {
assemble();
});
function randomX() {
return Math.floor(Math.random() * $(window).width() * 0.7) - $(window).width() * 0.25;
}
function randomY() {
return Math.floor(Math.random() * $(window).height() * 0.7) - $(window).height() * 0.25;
}
function scatter() {
for (var i = 0; i < 3; i++) {
$(".mutable > span:eq(" + i + ")").animate({
left: randomX(),
top: randomY(),
color: "#f118"
}, 2000, "easeInBack");
}
}
function assemble() {
for (var i = 0; i < 3; i++) {
$(".mutable > span:eq(" + i + ")").animate({
left: '0px',
top: '0px',
color: "#000"
}, 2000, 'easeOutBounce');
}
}
</script>
<style>
span {
font-size: 30px;
}
.mutable {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.mutable span {
position: relative; /* <--- */
}
</style>
</head>
<body>
<div class="mutable">
<span>a</span>
<span>b</span>
<span>c</span>
</div>
<button onclick="scatter()">Scatter</button>
<button onclick="assemble()">Assemble</button>
</body>
</html>
The sandbox demo you want to emulate is using an animation they call Alphabet Soup. After a quick google search, I found an npm package that can help you implement this in your react component: https://github.com/OrigenStudio/react-alphabet-soup.
Why reinvent the wheel?

Create a tile with `div` that would fill all the screen

I'm trying to create a tile with random colors in a list that would fill the entire page.
There's a couple of questions that I want to ask here. The random_color variable only applies to the first box and not the second box. Also, how would I dynamically and automatically create divs that would have random background colors, just like the first box?
var version_1 = ['#111111', '#222222', '#333333', '#444444'];
var random_color = version_1[Math.floor(Math.random() * version_1.length)];
document.getElementById('box').style.background = random_color;
html, body {
margin: 0;
padding: 0;
}
#box {
width: 5%;
padding-bottom: 5%;
background: #00F;
color: #FFF;
position: relative;
}
#innerContent {
position: absolute;
left: 10px;
right: 10px;
top: 10px;
bottom: 10px;
}
<div id="box">
<div id="innerContent">
Box 1
</div>
</div>
<div id="box">
<div id="innerContent">
Box 2
</div>
</div>
Id is not for multiple elements. It should be unique in the document. I recommend you a 'class' for this.
<div class="box">
<div class="innerContent">
Box 1
</div>
</div>
<div class="box">
<div class="innerContent">
Box 2
</div>
</div>
Also if you want to dynamically create div elements, use document.createElement("div"). This will return HTMLElement object and you can manipulate it with javascript.
const newDiv = document.createElement("div");
newDiv.className = "box";
newDiv.innerText = "New Box!";
newDiv.style.backgroundColor = "#ff8888";
Finally, for a random background colors, you should use Math.random() properly. Because, Math.random() returns random value between 0 and 1, so if you want to use them as color, you should multiply 255 to them.
const r = Math.random() * 255;
const g = Math.random() * 255;
const b = Math.random() * 255;
div.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
You want to change the id to a class. Secondly, you want to change you logic for determining color as right now, every box will be the same color since random_color is only being determined once. Try this
const version_1 = ['#111111', '#222222', '#333333', '#444444'];
const boxEls = document.getElementsByClassName('box');
for (let el of boxEls) {
let random_color = version_1[Math.floor(Math.random() * version_1.length)];
el.style.backgroundColor = random_color;
}
With this code above, you are running the Math.random() for each box.
Use querySelectorAll to select all the divs with box id and then change the backgroundColor of every box using forEach loop.
var version_1 = ['#111111', '#222222', '#333333', '#444444'];
var random_color = version_1[Math.floor(Math.random() * version_1.length)];
let boxes = document.querySelectorAll('#box');
boxes.forEach((box) => box.style.backgroundColor = random_color);
html, body {
margin: 0;
padding: 0;
}
#box {
width: 5%;
padding-bottom: 5%;
background: #00F;
color: #FFF;
position: relative;
}
#innerContent {
position: absolute;
left: 10px;
right: 10px;
top: 10px;
bottom: 10px;
}
<div id="box">
<div id="innerContent">
Box 1
</div>
</div>
<div id="box">
<div id="innerContent">
Box 2
</div>
</div>

how to make a div grow to cover a paragraph in javascript with transition?

I am trying to make a transition with a div that should grow and overlap a text.
Here are my codes
const box = document.querySelector("#box");
const mybutt = document.querySelector("#mybutt");
mybutt.addEventListener("click", transitionfunction);
function transitionfunction() {
if(box.style.height != "100px"){
box.style.height = "100px";
box.style.transition = "2s";
}
else {
box.style.height = "50px";
box.style.transition = "2s";
}
}
#box {
background: red;
height: 50px;
width: 50px;
}
#para {
postion: fixed;
}
<div id="parentdiv">
<div id="box"></div>
<p id="para">Help</p>
</div>
<button id="mybutt">click</button>
At the moment, on the click of the button, both the button and the paragraph para move down, I want them to be fixed and I want the div, #box to cover the para but its not working. I tried putting it to fixed but doesnt work. And on the click on the button again, it should reveal the text again.
If you use position: fixed;, you should manually set the top property.
To make a div overlay some text, use z-index
const box = document.querySelector("#box");
const mybutt = document.querySelector("#mybutt");
mybutt.addEventListener("click", transitionfunction);
function transitionfunction() {
if (box.style.height != "100px"){
box.style.height = "100px";
box.style.transition = "2s";
} else {
box.style.height = "50px";
box.style.transition = "2s";
}
}
#mybutt {
position: fixed;
top: 120px;
}
#box {
background: red;
position: fixed;
height: 50px;
width: 50px;
z-index: 2;
}
#para {
position: fixed;
z-index: 1;
top: 60px;
}
<div id="parentdiv">
<div id="box"></div>
<p id="para">Help</p>
</div>
<button id="mybutt">click</button>
Firstly, you spelled "position" wrong for #para. Change it to:
#para {
position: absolute;
top: 10%;
}
This will keep the paragraph positioned in one spot; it won't move.
Fixed will work, although you might want to use 'absolute' instead if you want it to anchored to it's parent instead of the window itself.
Also, 'position' is misspelled; not sure if it is in your testing code.
The 'top' property has to be set for the element to know where to anchor itself, the 'position' property is what to anchor to.
HTML
<div id="parentdiv">
<div id="box"></div>
<p id="para">Help</p>
</div>
</div>
<button id="mybutt">click</button>
CSS
<style>
#box {
background: red;
height: 50px;
width: 50px;
}
#para {
position: absolute;
top:70;
}
</style>
*You also might want to move '#para' outside '#parentdiv', but it depends what you'll trying to ultimately do, it does work inside too.
Added:
To include an alert at 75px, you have to use a function that gives you more granular control(as far as I know at least). This is one solution:
<script>
const box = document.querySelector("#box");
const mybutt = document.querySelector("#mybutt");
mybutt.addEventListener("click", transitionfunction);
var intHeight = $("#box").css("height").split("p")[0];
function transitionfunction() {
if(intHeight < 100) {
intHeight++;
$("#box").css("height", intHeight + "px");
if (intHeight===76)
alert("75px!")
requestAnimationFrame(transitionfunction);
}
intHeight = $("#box").css("height").split("p")[0];
mybutt.addEventListener("click", revtransitionfunction);
mybutt.removeEventListener("click", transitionfunction);
}
function revtransitionfunction() {
if(intHeight >= 50) {
intHeight--;
$("#box").css("height", intHeight + "px");
if (intHeight===74)
alert("75px!")
requestAnimationFrame(revtransitionfunction);
}
intHeight = $("#box").css("height").split("p")[0];
mybutt.addEventListener("click", transitionfunction);
mybutt.removeEventListener("click", revtransitionfunction);
}

How to check if there is any part of element on top using js?

I need to check for given element if there exists any point that elementFromPoint with this point given as argument will return this element. I do not need to find that point - just information if there is at least one. It is equivalent to "if there exists any part of the element which is not covered by another element".
Example: JSFiddle - since there is a part of num2 and num3 which is "on top" desired function should return true for those two. However for num1 result should be false.
I'm not sure if there's a better way than this brute force option of getting the element's boundaries using element.getClientRects(), and then iterating over that area with elementFromPoint to see if it ever returns that element:
var checkIfOnTop = function(el) {
console.log("Checking",el);
var rect = el.getClientRects()[0];
for (var x = rect.left; x <= rect.right; x++) {
for (var y = rect.top; y <= rect.bottom; y++) {
if (document.elementFromPoint(x, y) === el) {
console.log("This element is reachable by click at ",x,y);
return true;
}
}
}
console.log("This element is not reachable by click");
return false;
}
checkIfOnTop(document.getElementById('num1'));
checkIfOnTop(document.getElementById('num2'));
checkIfOnTop(document.getElementById('num3'));
#container { position: relative }
#num1 { position: absolute; left: 50px; height: 200px; width: 200px; background-color: blue}
#num2 { position: absolute; left: 50px; height: 200px; width: 200px; background-color: green}
#num3 { position: absolute; left: 100px; height: 200px; width: 200px; background-color: red}
<div id='container'>
<div id='num1'></div>
<div id='num2'></div>
<div id='num3'></div>
</div>
Not recommended for very large elements, of course.

Detecting if element is hovering on section

I have side dot navigation on website - standard position: fixed dot nav. Now i have also 2 types of sections with 2 types of background on same website - lets assume one are white and another black. My problem is that when dots are not visible when navigation is hovering black section. So i tried to write script that detects if section have certain class and if certain dot is on this section - if yes then color of dot is changed. I had certain success but after i finish i realise that my script works only in one way ( scrolling top to bottom ) and even if i will detect bottom to top scrolling it will not work properly when i change direction in the middle of website. I arleady spend on this quite a while and im clueless - here is code that i have so far - its working when you scroll from top to bottom.
Do you have any other suggestion or perhaps some library to solve this issue ?
EDIT: Layout is quite artistic - so there are some boxes that floats from left or right dynamicly and dots also have to change when box is there, its why i splice my array and push #myname to it.
EDIT2: You can see how it works under this link ( not optimized, slow load time http://lektor.ionstudio.pl/)
var sections = [];
$("section[id]").each(function() {
sections.push("#" + this.id);
})
sections.splice(0,0,"#myname");
i = 0;
$(window).scroll(function(){
var content = $("section.current").hasClass("white-section");
$("#banner .navigation li").each(function(){
var thisElem = this.getBoundingClientRect();
section = sections[i];
section = document.querySelector(section);
sectionRect = section.getBoundingClientRect();
if(sectionRect.top - thisElem.top <= 0) {
if($(section).hasClass("white")) {
$(this).addClass("black");
} else {
$(this).removeClass("black");
}
if(sectionRect.top + $(section).outerHeight(true) <= thisElem.top ) {
i++;
}
}
})
})
As mentioned in the comments i would use elementFromPoint to check if a dot it over a section with a specific class.
Try this:
(function(win, doc) {
var dotsSelector = '.dot';
var sectionsSelector = 'section';
var classes = ['white', 'black'];
var dots = [].slice.call(doc.querySelectorAll(dotsSelector));
var dotPositions = dots.map(function(dot) {
var rect = dot.getBoundingClientRect();
return rect.top + rect.height / 2 ;
});
var sections = [].slice.call(doc.querySelectorAll(sectionsSelector));
win.addEventListener('scroll', function(event) {
for(var i = 0; i < dots.length; ++i) {
var element = doc.elementFromPoint(0, dotPositions[i]);
var section = null;
while(!section && element) {
section = sections.find(function(s) { return s === element });
element = element.parentNode;
}
if(section) {
dots[i].classList.toggle(classes[0], section.classList.contains(classes[1]));
}
}
});
})(window, document);
section {
min-height: 400px;
background: white;
}
section.black {
background: black;
}
.menu {
position: fixed;
top: 50%;
transform: translateY(-50%);
left: 10px;
z-index: 20;
}
.dot {
border-radius: 50%;
width: 20px;
height: 20px;
background: black;
margin-bottom: 10px;
}
.dot.white {
background: white;
}
* {
padding: 0;
margin: 0;
}
ul {
list-style: none;
}
<section></section>
<section class="black"></section>
<section></section>
<section class="black"></section>
<section></section>
<section class="black"></section>
<ul class="menu">
<li class="dot"></li>
<li class="dot"></li>
<li class="dot"></li>
<li class="dot"></li>
<li class="dot"></li>
<li class="dot"></li>
</ul>
It only works on one way scroll because of the way you are looping. You need to remove the dependence of the 'i' increment in your function.
The only reason you are using this increment is because you need to be able to uniquely identify the section.
Are you able to add some meta data to "this" element that you could use to identify the section instead? Something like
<li data-sectionID="999" ....>Menu Node</li>
...
section = $(this).data("sectionID");

Categories

Resources