List items created through Javascript are not aligning properly - javascript

I need to display video and when user presses menu, I need to divide the screen to 2 halves vertically (adjacent to each other) and I need to display a text in middle (horizontally and vertically) of first half and need to display a list in the second half (this list also should be in middle of 2nd half horizontally and vertically). I created a parent div and 2 child divs with flex and adding list items dynamically through javascript. Code is as given below.
function displayMenu() {
var mid = document.getElementById('mid');
if (mid.classList.contains('hidden') == false) {
mid.classList.toggle("hidden");
return;
}
var ulid = document.getElementById('ulid');
for (let index = 0; index < 3; index++) {
let lItem = document.createElement('li');
lItem.style.width = '100%';
lItem.style.height = '150px';
lItem.style.borderStyle = 'solid';
lItem.style.borderWidth = '1px';
let img = document.createElement("img");
img.src = "img/TNT.png";
lItem.appendChild(img);
lItem.appendChild(document.createTextNode('FIRST'));
ulid.appendChild(lItem);
}
mid.classList.toggle("hidden");
}
function changeChannel(e) {
console.log('received keyEvent : ' + e.keyCode);
let keyCode = e.keyCode;
if (keyCode == 77) {
displayMenu();
}
}
document.addEventListener('keydown', changeChannel);
displayMenu();
<!DOCTYPE html>
<html>
<head>
<script src='js/index.js'>
</script>
<style>
html,
body {
height: 100%
}
#vid {
position: fixed;
top: 50%;
left: 50%;
z-index: -1;
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
transform: translate(-50%, -50%);
}
#mid {
opacity: 0.5;
display: flex;
height: 100vmin;
justify-content: stretch;
flex-flow: row nowrap;
z-index: 2;
}
#mid.hidden {
display: none;
}
#mid1,
#mid2 {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
#mid1 {
background-color: rgba(255, 255, 255, 0.5);
}
#mid2 {
background-color: rgba(255, 255, 255, 0.6);
}
#ulid {
list-style-type: none;
width: 100%;
height: 150px;
}
</style>
</head>
<body>
<video id='vid' src='textMotion.mp4' autoplay loop></video>
<div id='mid' class='hidden'>
<div id='mid1'>
<h1>TEXT</h1>
</div>
<div id='mid2'>
<ul id='ulid'></ul>
</div>
</div>
</body>
</html>
But I am facing multiple issues when I ran this as below.
The whole list is not centered vertically and horizontally in the second half.
The list Item is not starting from starting of second half.
Image on the list is not getting displayed from left of the list.
Text on the item is not getting displayed in the center of the list vertically.
Screen shot is as below.
Can any one please help me to fix these issues?

As noted in the comments, it is a little hard to know exactly what you want without an image. But I think this might be moving things in the right direction.
The fixes are as follows
1. add a class to the list item and give it flex properties that make it left-justified (justify-content: flex-start) and vertically centered (align-items: center).
2. update the flex properties for the #mid1 and #mid2 elements and align-self on #mid1 to get the text centered in that box.
CSS changes
.list-item {
display:flex;
align-items: center;
justify-content: flex-start;
}
#mid1,
#mid2 {
flex: 1;
display: flex;
justify-content: center;
align-items: flex-start;
text-align: center;
}
#mid1 {
background-color: rgba(255, 255, 255, 0.5);
align-self: center;
}
Javascript changes
for (let index = 0; index < 3; index++) {
let lItem = document.createElement('li');
lItem.style.width = '100%';
lItem.style.height = '150px';
lItem.style.borderStyle = 'solid';
lItem.style.borderWidth = '1px';
lItem.classList.add( "list-item" ); // Add a list-item class
...
}
You can see the results here https://codepen.io/bunnymatic/pen/vjobQp. I grabbed a TNT image from Google as a placeholder.
Hope this helps

Related

overflow-anchor doesn't work for horizontal scrolling

I would like to build an infinite horizontal scroll that scrolls in both directions - left and right. As user scrolls to the left, new content is prepended to the scrollable element (think scrolling through a schedule history, for example). As they scroll to the right, content is appended.
I have learned that browsers anchor content when scrolling up and down which is fantastic, exactly what I'd expect. The effect of that is that prepending content to the scrolled element anchors user to their current, logical position and the content doesn't "jump".
But the anchoring doesn't seem to work when scrolling left or right. The behaviour is as if I set overflow-anchor: none. What can I do to make it work as well as when scrolling up?
let topCounter = 0;
document.querySelector('.scrollable-top').scrollTo({ top: 100 });
document.querySelector('.scrollable-top').onscroll = (event) => {
if (event.target.scrollTop < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${topCounter--}`;
document.querySelector('.scrollable-top').prepend(box);
}
};
let leftCounter = 0;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
if (event.target.scrollLeft < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${leftCounter--}`;
document.querySelector('.scrollable-left').prepend(box);
}
};
.scrollable-top {
width: 200px;
height: 250px;
overflow-y: auto;
border: solid 1px black;
}
.scrollable-left {
display: flex;
width: 250px;
overflow-x: auto;
border: solid 1px black;
}
.content-box {
flex: 1 0 auto;
height: 150px;
width: 150px;
border: solid 1px red;
display: flex;
justify-content: center;
align-items: center;
}
<div class="scrollable-top">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
<div class="scrollable-left">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
Scroll the horizontal container to the right by 150 using scrollBy(150, 0):
let topCounter = 0;
document.querySelector('.scrollable-top').scrollTo({ top: 100 });
document.querySelector('.scrollable-top').onscroll = (event) => {
if (event.target.scrollTop < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${topCounter--}`;
document.querySelector('.scrollable-top').prepend(box);
}
};
let leftCounter = 0;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
if (event.target.scrollLeft < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${leftCounter--}`;
document.querySelector('.scrollable-left').prepend(box);
// solution ------------------
event.target.scrollBy(150, 0);
}
};
.scrollable-top {
width: 200px;
height: 250px;
overflow-y: auto;
border: solid 1px black;
display: inline-block;
float:left;
}
.scrollable-left {
display: flex;
width: 250px;
overflow-x: auto;
border: solid 1px black;
float:right;
}
.content-box {
flex: 1 0 auto;
height: 150px;
width: 150px;
border: solid 1px red;
display: flex;
justify-content: center;
align-items: center;
}
<div class="scrollable-top">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
<div class="scrollable-left">
<div class="content-box">1</div>
<div class="content-box">2</div>
<div class="content-box">3</div>
</div>
As per the specs, following is the intent behind such anchoring:
Changes in DOM elements above the visible region of a scrolling box can result in the page moving while the user is in the middle of consuming the content.
This spec proposes a mechanism to mitigate this jarring user experience
by keeping track of the position of an anchor node and adjusting the scroll offset accordingly.
This spec also proposes an API for web developers to opt-out of this behavior.
Since no page loads horizontally, I think they didn't implement this for horizontal scrollbars. Also, apart from above use case it makes no sense to implement this behavior.
Note: Safari doesn't implement the overflow-anchor behavior. So, your code for vertical scroll fails in Safari.
I've tried my code, for horizontal scrolling, on Safari and it works. So incase you want to implement infinite vertical scroll, and want to support all the browsers, then you'll have to optout of overflow-anchor behavior and use scrollBy(x,y) to do it manually. :(
I tried to fix your code and got this option
let leftCounter = -2;
let rightCounter = 2;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
if (event.target.scrollLeft < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${leftCounter--}`;
document.querySelector('.scrollable-left').prepend(box);
event.target.scrollLeft += 250
}
if ((event.target.scrollWidth - event.target.scrollLeft - 250) < 100) {
let box = document.createElement('div');
box.className = 'content-box';
box.textContent = `${rightCounter++}`;
document.querySelector('.scrollable-left').append(box);
}
};
.scrollable-top {
width: 200px;
height: 250px;
overflow-y: auto;
border: solid 1px black;
}
.scrollable-left {
display: flex;
width: 250px;
overflow-x: auto;
border: solid 1px black;
}
.content-box {
flex: 1 0 auto;
height: 150px;
width: 150px;
border: solid 1px red;
display: flex;
justify-content: center;
align-items: center;
}
<div class="scrollable-left">
<div class="content-box">-1</div>
<div class="content-box">0</div>
<div class="content-box">1</div>
</div>

Make both overlapping divs clickable?

Is it possible to make two overlapping divs, both clickable?
I've appended divs to two containers, #container and #container2. Their styles are exactly the same only except one is flex-direction: column; and one is flex-direction: column;. Both position:absolute with #container2 on top. I made each of the appended child clickable to fill its background color. Only the div on top is clickable so far, is there a way to make both clickable? or is there another way to have the bottom div react to my clicks?
window.addEventListener('load', init);
function init() {
calculateGrid();
//calculate grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
$(".grid").click(function() {
$(this).toggleClass('selected');
});
};
#container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: column;
overflow: hidden;
}
#container .grid {
border: 1px solid blue;
}
#container2 {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: row;
overflow: hidden;
}
#container2 .grid {
border: 1px solid red;
}
.grid {
font-size: 10px;
color: white;
}
#container .selected {
background-color: blue;
}
#container2 .selected {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="wrapper">
<div id="container"></div>
<div id="container2"></div>
</div>
View on CodePen
One method is to use Document.elementsFromPoint() to return "an array of all elements at the specified coordinates". Iterate through that array, adding the "selected" class to "grid" elements.
window.addEventListener('load', init);
function init() {
// build grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container1').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
// handle grid clicks
function handleGridClick(e) {
let elms = document.elementsFromPoint(e.clientX, e.clientY);
Array.from(elms).forEach(elm => {
if (elm.classList.contains('grid'))
elm.classList.add('selected');
});
}
// initialize grid and click handler
calculateGrid();
document.addEventListener('click', handleGridClick);
};
.container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
overflow: hidden;
}
#container1 {
flex-direction: column;
}
#container1 .grid {
border: 1px solid blue;
}
#container1 .grid.selected {
background-color: blue;
}
#container2 .grid {
border: 1px solid red;
}
#container2 .grid.selected {
background-color: red;
}
<div id="wrapper">
<div id="container1" class="container"></div>
<div id="container2" class="container"></div>
</div>
You can't actually hover two items at the same time in plain 'ol HTML/CSS - for that you will need JavaScript as explained in the accepted solution. However, there's a CSS-only solution to allow hovering over the different layers, which was fun to figure out at the very least.
So the idea is that you have these invisible boxes on top of the visible ones. The invisible boxes only have borders such that any time your mouse hits a border, some clever z-index swapping takes place to make the visible containers change their stacking order.
For every .grid item you need to create a corresponding .box item: https://jsfiddle.net/ryanwheale/01v5yz86/93/

How do i center two divs vertically in Javascript?

Here is the code snippet:
var wrapper = document.createElement('DIV');
wrapper.setAttribute("width", x * rows);
wrapper.setAttribute("height", y * columns);
wrapper.align = "center";
var buttonWrap = document.createElement('DIV');
buttonWrap.setAttribute("style", "clear:float");
As you can see in my code snippet, I have tried to center my div. But this code doesn't work. What works is making both divs fixed. But at the end of the day, the second div will then be upon the first div.
Please help.
If you can use only CSS I would do it this way:
.container {
width: 500px;
height: 150px;
border: solid black 1px;
/* Align center */
display: flex;
justify-content: center;
align-items: center;
}
.small {
background-color: red;
width: 100px;
height: 30px;
}
.big {
background-color: green;
width: 100px;
height: 100px;
}
<div class="container">
<div class="small"></div>
<div class="big"></div>
</div>
If you want to do it in javascript, apply the style written above in CSS this way:
var container = document.getElementsByClassName('container')[0];
container.style.display = "flex";
and so on...

Can't figure out where this #text is coming from when using node.firstChild()

I am trying to get the node.firstChild of the #root element, after generating content within the container. I expect it to be the first div, because when I look at the elements in the dev console, that's the first child that I see. I am not sure where this #text is coming from, or what it means even.
Please help me understand:
What #text is (obviously it's some type of text, but I don't see it)
Why it's showing up instead of the firstChild of my container which should actually be div.each-result
It should be noted that I am running this code in CodePen
I am also aware I can also use Node.firstElementChild, but I want to understand what's going wrong currently.
const leftArrow = document.querySelector('#left-arrow');
const rightArrow = document.querySelector('#right-arrow');
const rootDiv = document.querySelector('#root');
const generateButton = document.querySelector("#button-generate");
//This code basically generates the content within the div
generateButton.addEventListener("click", () => {
for (let i = 0; i < 10; i++) {
const newDiv = document.createElement("div");
newDiv.classList.add("each-result");
newDiv.appendChild(addImg("https://uk.usembassy.gov/wp-content/uploads/sites/16/please_read_icon_150x150.jpg"));
rootDiv.appendChild(newDiv);
}
console.log(rootDiv.firstChild);
});
//These enable the arrow to scroll through the dynamically generated content
leftArrow.addEventListener('click', () => {
//use
});
rightArrow.addEventListener('click', () => {
alert("right arrow works");
});
//Simple function to create and image element with the src attribute set in one line
function addImg(url) {
const newImg = document.createElement("img");
newImg.setAttribute("src", url);
return newImg;
}
html, body {
height: 100%;
}
button {
position: relative;
z-index: 1
width: auto;
height: 50px;
}
.container {
display: flex;
justify-content: center;
position: relative;
top: 15%;
z-index: 0
}
.result-container {
display: flex;
align-items: center;
border: 1px solid black;
width: 80%;
height: 200px;
position: relative;
flex-flow: row no-wrap;
overflow: hidden;
}
.each-result {
height: 150px;
width: 150px;
border: 3px dotted red;
margin: 1%;
}
img {
height: 100%;
width: auto;
}
.nav-arrows {
display: flex;
justify-content: space-between;
width: 100%;
height: auto;
position: absolute;
background: clear;
pointer-events: none;
}
#left-arrow, #right-arrow {
pointer-events: auto;
}
<script src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
<div class="container">
<div class="nav-arrows">
<button id="left-arrow"><i class="fas fa-arrow-alt-circle-left"></i>
</button>
<button id="right-arrow"> <i class="fas fa-arrow-alt-circle-right"></i>
</button>
</div>
<div id="root" class="result-container">
</div>
</div>
<button id="button-generate">Generate Content</button>
Look at the first example here: Node.firstChild
In the above, the console will show '#text' because a text node is
inserted to maintain the whitespace between the end of the opening <p>
and <span> tags. Any whitespace will create a #text node, from a
single space to multiple spaces, returns, tabs, and so on.
Another #text node is inserted between the closing </span> and
</p> tags.
If this whitespace is removed from the source, the #text nodes are not
inserted and the span element becomes the paragraph's first child.
As you suggested yourself, ParentNode.firstElementChild is the best way to go in this case.

Hover on image to add overlay in JavaScript

If I have more than one image in a div wrapper. I want to add overlay on image when user hover over the image. I am trying to do using code shown below.
for(var i=0; i<document.getElementsByTagName('img').length; i++) {
document.getElementsByTagName('img')[i].addEventListener('mouseover', function(event){
let elementExists = document.getElementById('wrapper');
let Center = document.createElement('div');
let Text = document.createElement('div');
if (!elementExists) {
let Wrapper = document.createElement('div');
let parentElement = event.currentTarget.parentElement;
Wrapper.classList.add('Wrapper');
Wrapper.id = 'wrapper';
Center.classList.add('Center');
Text.innerHTML = "Sample text";
parentElement.appendChild(Wrapper);
Wrapper.appendChild(Center);
Center.appendChild(Text);
Wrapper.addEventListener('mouseout', function(event){
if (document.getElementById('wrapper')) {
document.getElementById('wrapper').remove();
}
});
}
});
}
.col-md-6 {
width: 375px;
height: 211px;
margin: 20px;
position: relative;
}
.Wrapper {
display: table;
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
height: 100% !important;
width: 100%;
text-align: center;
z-index: 1000;
font-family: arial;
color: #fff;
top: 0;
}
.Center {
display: table-cell;
vertical-align: middle;
}
<div class="col-md-6">
<a href="#">
<img src="https://www.blog.google/static/blog/images/google-200x200.7714256da16f.png" />
</a>
<a href="#">
<img src="https://www.blog.google/static/blog/images/google-200x200.7714256da16f.png" />
</a>
</div>
Every time I hover on first image, code just works fine. But when I hover on 2nd image it adds overlay on 1st image only.(It should add overlay on second image) I tried to debug the code and let parentElement = event.currentTarget.parentElement; is showing the a href only.
NOTE: I came to know its because I am giving position: absolute to Wrapper. I only want to make changes in JavaScript file and at max to css.
Please let me know if you found error in the code.
It's simply a css problem. Just add this to what you currently have:
a {
position: relative;
display: inline-block;
}
.Wrapper {
display: inline-block;
left: 0;
}
.Center {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
}
Also I removed the final Text div and added its text to the Center div, as it triggered the mouseout event and made it flicker.
for(var i=0; i<document.getElementsByTagName('img').length; i++) {
document.getElementsByTagName('img')[i].addEventListener('mouseover', function(event){
let elementExists = document.getElementById('wrapper');
let Center = document.createElement('div');
if (!elementExists) {
let Wrapper = document.createElement('div');
let parentElement = event.currentTarget.parentElement;
Wrapper.classList.add('Wrapper');
Wrapper.id = 'wrapper';
Center.classList.add('Center');
Center.innerHTML = "Sample text";
parentElement.appendChild(Wrapper);
Wrapper.appendChild(Center);
Wrapper.addEventListener('mouseout', function(event){
if (document.getElementById('wrapper')) {
document.getElementById('wrapper').remove();
}
});
}
});
}
.col-md-6 {
width: 375px;
height: 211px;
margin: 20px;
position: relative;
}
a {
position: relative;
display: inline-block;
}
.Wrapper {
display: inline-block;
left: 0;
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
height: 100% !important;
width: 100%;
text-align: center;
z-index: 1000;
font-family: arial;
color: #fff;
top: 0;
}
.Center {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
}
<div class="col-md-6">
<a href="#">
<img src="https://www.blog.google/static/blog/images/google-200x200.7714256da16f.png" />
</a>
<a href="#">
<img src="https://www.blog.google/static/blog/images/google-200x200.7714256da16f.png" />
</a>
</div>

Categories