React Virtualized position of list item - javascript

I'm using React Virtualized in a similar setup like the snippet below. There are a couple of items that are rendered using a CellMeasurer. The last item indicates that more items will be loaded and is not rendered normally. This item has the wrong position/style and the wrong height associated with it. How can I fix this?
If you want to play with it, here is a Plunkr:
https://plnkr.co/edit/TrKdNu4FfNsXqERPVnYo?p=preview
var List = ReactVirtualized.List;
var CellMeasurer = ReactVirtualized.CellMeasurer;
var CellMeasurerCache = ReactVirtualized.CellMeasurerCache;
var cache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 50
});
// List data as an array of strings
var namesList = [
'Brian Vaughn',
'Bob Smith',
'Someone Else',
'I hate making up names for the sake of names.'
// And so on...
];
var App = React.createClass({
getInitialState: function() {
return {
list: namesList
};
},
render: function() {
return <List
className='List'
width={300}
height={300}
rowCount={this.state.list.length + 1}
rowHeight={cache.rowHeight}
deferredMeasurementCache={cache}
list={this.state.list}
rowRenderer={
({ index, isScrolling, key, parent, style }) => {
var item = this.state.list[index];
let result;
if (!item) {
console.log(index);
result =
<div className='Row'
style={style}
key={key}
>Loading!!!
</div>; }
else
result = <CellMeasurer
cache={cache}
columnIndex={0}
key={key}
style={style}
rowIndex={index}
parent={parent}>
{
<div
className='Row'
key={key}
style={style}
>
{this.state.list[index]}
</div>
}
</CellMeasurer>;
return result;
}}/>;
}
});
// Render your list
ReactDOM.render(
<App/>,
document.getElementById('example')
);
.List {
border: 1px solid black;
box-sizing: border-box;
}
.Row {
width: 100%;
height: 100%;
border-bottom: 1px solid black;
display: flex;
align-items: center;
padding: 0 0.5rem;
box-sizing: border-box;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react/dist/react-with-addons.min.js"></script>
<script src="https://unpkg.com/react-dom/dist/react-dom.min.js"></script>
<script src="https://unpkg.com/react-virtualized/dist/umd/react-virtualized.js"></script>
<link rel="stylesheet" href="https://unpkg.com/react-virtualized/styles.css">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="example">
<!-- This element's contents will be replaced with your code... -->
</div>
<script src="script.js"></script>
</body>
</html>

CellMeasurer wasn't intended for use with only certain rows in this way. From an external POV I understand why it looks like it should just work though.
When Grid renders a cell- if it thinks CellMeasurer is being used- then it positions the cell at top:0, left:0 to give it space within the Grid/List to expand. (The size of its container constrains its size, even though it's absolutely positioned. I'm not sure why this is to be honest.)
So in your case, Grid sees the last row hasn't been measured, think it's supposed to be, and positions it at 0,0.
The simplest solution for now would be to just wrap that row in CellMeasurer too.

Related

How to change css property in react

I want to change css width property of my element on some condition
<div className="consoleLayoutRoot-sideMenu">
<ConsoleSideMenu />
</div>
css
.consoleLayoutRoot-sideMenu .ant-menu {
padding-top: 30px;
/* background-color: #191146 !important; */
}
I am doing this way..but nothing is happening
document.getElementsByClassName("conjnjnot-sideMenjnjbhbhu.annjn ").style.width = "77px";
That's not working because you're treating a list as though it were an element. But it's also fundamentally not how you would do this in a React project.
Instead, you'd have the component re-render when the condition becomes true (perhaps by setting a state member). When rendering the div, you optionally include a style or a class name depending on whether you want the width applied:
<div className={`consoleLayoutRoot-sideMenu ${shouldHaveWidthClass ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
...where .width-class { width: 50px; } is in your stylesheet.
Or with inline style, but inline styles are best avoided:
<div className="consoleLayoutRoot-sideMenu" style={shouldHaveWidthSetting ? { width: "50px" } : undefined}>
<ConsoleSideMenu />
</div>
Here's an example (using a class);
const {useState} = React;
const ConsoleSideMenu = () => <span>x</span>;
const Example = () => {
const [includeWidth, setIncludeWidth] = useState(false);
const toggle = ({currentTarget: { checked }}) => {
setIncludeWidth(checked);
};
return <React.Fragment>
<div className={`consoleLayoutRoot-sideMenu ${includeWidth ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
<label>
<input type="checkbox" onChange={toggle} checked={includeWidth} />
Include width class
</label>
</React.Fragment>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
.width-class {
width: 50px;
}
.consoleLayoutRoot-sideMenu {
border: 1px solid blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Prevent focus on Expand More button after content is inserted in React

I need to list out a long name list inside my page while showing all names at first is not desirable.
So I try to add an expand more button on it.
However, using a button will keep the browser focus on that button after it's pressed, left the button position unchanged on the screen while the name was inserted before that button.
On the other hand, using any, not focusable element (eg. div with onclick function) will do the desired behavior but lost the accessibility at all. Making the "button" only clickable but not focusable.
How do I make the button flushed to list bottom like the snippet div block does? Or is there a better choice to expand the existing list?
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Removing focus from the button in the click handler is probably the most elegant approach: e.target.blur(). It will work on any HTML element, whether it is focusable or not (as with the div in your case).
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
e.target.blur()
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Inspired by #MiKo, temporally unmount the button after click and set a timeout to add it back seems to do the work. Since browser lose the focus on original expand button, this will keep content flush down without focusing the original button:
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const [showBtn, setShowBtn] = React.useState(true)
const handleExpand = e => {
setShowBtn(false)
setIdx(idx + 1)
setTimeout(() => setShowBtn(true), 10)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
{showBtn?
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div> :
<div></div>
}
</div>
}
But I'm still looking a method that doesn't need to 'unmount' a thing which should be there all time.

how to properly address a state change in ReactJS

good people.
i have a small program that adds table row elements, when "Add" is clicked. There is also an color change option when table cell is clicked. The problem is - when multiple elements are created, clicking on one of them, changes the color for all of them, though i have a onClick sitting only on the TD tags. How could this be fixed?
https://jsfiddle.net/mattighof/5nmcyL7b/
<table>
{this.state.list.map((element, index) => {
return (
<tr>
<td
className={this.state.textColor ? "trRed" : "trBlack"}onClick={this.handleClick}>
{element}
<div
onClick={e => this.removeElement(e, index)}
className="div"
/>
</td>
</tr>
);
})}
</table>
would highly appreciate your advice.
Since you're generating <td> elements in an anonymous function, using this inside it will refer to the parent closure, which, in this case, is the Table component. Therefore, the textColor property is local to the component itself, and not to the individual <td> elements.
You're already iterating through a list that you keep in the component state, so you can slightly change the element structure to allow you to keep arbitrary state data individually.
To do this, instead of adding a raw string to your list, add an object with the text and isSelected properties set to the desired values, and when rendering the <td> elements or changing colors, use the said properties. You can, of course, name these properties to your liking, and even add more properties to individually manage the states of your elements.
One other thing to note is that the current implementation of the handleClick function is unaware of the context that you're calling it from, so you'll also need to pass the index of the element when calling it, and update your state with a new list where the element at the specified index has its state updated.
Here's the revised functions as per my naming:
addElement() {
this.setState({
list: this.state.list.concat({
text: "element",
isSelected: false
})
});
}
handleClick(e, index) {
if (!this.state.list[index]) {
return;
}
// to avoid any side effects, we're taking the immutable data approach
// and creating a new list with intended values, rather than updating the list directly
const oldElement = this.state.list[index];
const newElement = Object.assign({}, oldElement, {isSelected: !oldElement.isSelected});
const newList = [].concat(this.state.list);
newList.splice(index, 1, newElement);
this.setState({
list: newList
});
}
...
{this.state.list.map((element, index) => {
return (
<tr>
<td
className={element.isSelected ? "trRed" : "trBlack"}
onClick={e => this.handleClick(e, index)}
>
{element.text}
<div
onClick={e => this.removeElement(e, index)}
className="div"
/>
</td>
</tr>
);
})}
...
I've also forked your fiddle and updated it with the code blocks I mentioned above: https://jsfiddle.net/w76frtgx/
There are many ways to handle this. Instead of relying on state to change the class, simply toggle the class trRed on the clicked target element.
To achieve this, modify handleClick to this:
handleClick(e) {
e.target.classList.toggle("trRed")
}
Edit the style rule trRed to this:
.trRed {
color: red;
}
And finally remove the textColor: true from state since it will no longer be used.
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
list: []
};
this.handleClick = this.handleClick.bind(this);
this.addElement = this.addElement.bind(this);
this.removeElement = this.removeElement.bind(this);
}
handleClick(e) {
e.target.classList.toggle("trRed")
}
addElement() {
this.setState({ list: this.state.list.concat("element") });
}
removeElement(e, index) {
e.stopPropagation();
this.setState({ list: this.state.list.filter((_, i) => index !== i) });
}
render() {
return (
<div className="container">
<button onClick={this.addElement} type="button">
Add
</button>
<table>
{this.state.list.map((element, index) => {
return (
<tr>
<td
onClick={this.handleClick}
>
{element}
<div
onClick={e => this.removeElement(e, index)}
className="div"
/>
</td>
</tr>
);
})}
</table>
</div>
);
}
}
ReactDOM.render(<Table />, document.getElementById("app"));
body {
padding: 20px;
}
td {
border: 1px solid black;
height: 15px;
padding: 5px;
}
tr {
border: 1px solid black;
position: relative;
}
table {
margin-top: 10px;
text-align: center;
width: 70px;
border: 1px solid black;
background-color: beige;
border-collapse: collapse;
}
.trRed {
color: red;
}
.div {
float: right;
width: 6px;
height: 6px;
background-color: red;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

How to blur the whole body except a list item?

I wanted to create an effect where the whole body gets blurred or dimmed and only a particular list item appears clear. However when I set the z-index to the list item, it doesn't work. And when I set the z-index of the whole un-ordered list, it works but the all the list items appear clear (which I don't want).
Let me show you my html code:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ashish Toppo</title>
<link href="https://fonts.googleapis.com/css?family=Oxanium&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
</head>
<body >
<!-- the html for the top bar starts here -->
<div class="top_bar" id="topBar">
<div class="logo_name" id="logoName">Ashish Toppo</div>
<ul class="menu">
<li class="menu_items currently_active_menuItem" id="home">home</li>
<li class="menu_items" id="about">about</li>
<li class="menu_items" id="education">education</li>
</ul>
</div>
<!-- the html for the top bar ends here -->
<!-- the html for the intro starts here -->
<div class="intro" id="intro">
<div class="profile_pic" id="profilePic">
<img id="profileImg" src="images/ashish-toppo-green.jpg" width="100%" height="100%" alt="a picture of mine">
</div>
<div class="intro_box" id="introBox">
<!-- some introduction text here -->
<center id="aboutPointer">To know more about me, go to the about section!</center>
</div>
</div>
<!-- the html for the intro ends here -->
<script src="js/uiversal.js"></script>
<script src="js/index.js"></script>
</body>
</html>
Now, the Universal javaScript file:
/* this is a reusable js file universal to all web pages */
/* Ashish Toppo */
"use strict";
function get(id_or_class){
var obj = {
element: ( document.getElementById(id_or_class) ) ? document.getElementById(id_or_class) :
( document.getElementsByClassName(id_or_class) ) ? document.getElementsByClassName(id_or_class) :
( document.querySelector(id_or_class) ) ? document.querySelector(id_or_class) :
console.error("The provided HTML element could not be found"),
html: () => { return obj.element; },
changeText: (text) => { obj.html().innerHTML = text; },
appendText: (text) => {
let appendOn = obj.html().innerHTML;
obj.html().innerHTML = appendOn + text;
},
previousDisplayMode: "block",
hide: () => {
obj.previousDisplayMode = obj.html().style.display;
obj.html().style.display = "none";
},
show: () => {
obj.html().style.display = obj.previousDisplayMode;
},
on: (event, callBack) => {
obj.html().addEventListener(event, callBack);
},
previousZIndex: 1,
focusOn: () => {
let blur = document.createElement("div");
blur.className = "theDivThatBlurs";
blur.style.width ="100vw";
blur.style.height ="100vh";
blur.style.display ="block";
blur.style.position ="fixed";
blur.style.top ="0";
blur.style.left ="0";
blur.style.zIndex ="9";
blur.style.backgroundColor ="rgba(0, 0, 0, 0.9)";
blur.innerHTML = "";
document.body.appendChild(blur);
obj.html().style.zIndex = "100";
}
}
return obj;
}
and the index.js file was as followed:
/* my css wasn't working as i wanted, so i had to fix it using js */
"use strict";
(function(d){
const active = d.getElementsByClassName("currently_active_menuItem");
active[0].style.textDecoration = "none";
})(document);
var about = get("about");
var aboutPointer = get("aboutPointer");
aboutPointer.on("click", function(){
console.log("the about pointer has been clicked");
focus(about);
});
function focus(theElement){
console.log("the focus is working");
theElement.focusOn();
}
You can use the box-shadow property to achieve the dimming effect. Quick and easy :)
Just toggle a class programmatically and it should work for any element you have.
Code
function focusAndDim() {
document.getElementById("maindiv").classList.toggle("visible");
// if you want to get more fancy ;)
document.getElementsByTagName("body")[0].classList.toggle("blur");
}
.visible {
box-shadow: 0 0 0 10000px #ccc;
/* this code below make everything else hidden */
/* box-shadow: 0 0 0 10000px #fff; */
position: relative;
}
.btn {
height: 20px;
line-height: 1.4;
border: 2px solid #999;
padding: 12px 24px;
margin-bottom: 10px;
border-radius: 2px;
cursor: pointer;
}
body {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
height: 100vh;
}
body.blur div {
filter: blur(2px);
}
body.blur div.visible {
filter: blur(0);
}
<div class="btn" onclick="focusAndDim()" id="maindiv">Click Me</div>
<div>Other elements</div>

Onsen Cards not showing?

I'm trying to use JavaScript to place cards for however many ids there are in a given JSON input, and while the divisions and cards exist in HTML, they don't show in practice. Can anyone shed some light on this problem?
I'm working on a college project, and for that we need to create an app in Cordova. Our team uses Onsen UI as a framework.
EDIT: It seems as though the division "page__background page--material__background" is obstructing my cards. Basically, they're being rendered behind it. I tried z-index in CSS and that didn't do anything. Anyone know how to get these cards to draw on top.
<ons-page id="policies-page">
<style>
.intro {
text-align: center;
padding: 0 20px;
margin-top: 40px;
}
ons-card {
cursor: pointer;
color: #333;
}
.card__title,
.card--material__title {
font-size: 20px;
}
</style>
<script>
var jojo;
var insertnode1, insertnode2, insertnode3;
function cardGenerator() {
fetch('https://api.thesmokinggnu.net/api/policies')
.then(response => response.json())
.then(data=>{
console.log(data)
// Work with JSON data here
jojo = document.getElementById("policies-page");
for (policy of data.policies){
insertnode2 = document.createElement("DIV");
insertnode2.innerHTML = "<ons-card id='policycards' onclick='fn.pushPage({`id`: `policy_read.html`, `title`: `ID: "+policy.id+"`})'>"+policy.policyname+"</ons-card>"
jojo.appendChild(insertnode2);
}
})
}
cardGenerator();
//<ons-card onclick='fn.pushPage({`id`: `policy_read.html`, `title`: `ID: "+policy.id+"`})'>"+policy.policyname+"</ons-card>
</script>
</ons-page>
</template>```
In theory; this code, with the typical JSON input of 6 ids, should output 6 cards with the ID and other information. Instead, no cards. Not even an error.
Create div inside ons-page and add your cards inside this div:
<ons-page id="policies-page">
<div id="cards"></div>
<style>
...
</style>
<script>
var jojo;
var insertnode1, insertnode2, insertnode3;
function cardGenerator() {
fetch('https://api.thesmokinggnu.net/api/policies')
.then(response => response.json())
.then(data=>{
console.log(data)
// Work with JSON data here
jojo = document.getElementById("cards");
for (policy of data.policies){
insertnode2 = document.createElement("DIV");
insertnode2.innerHTML = "<ons-card ...</ons-card>"
jojo.appendChild(insertnode2)
}
})
}
cardGenerator();
</script>
</ons-page>
Here is a working snippet example:
var jojo;
var insertnode1, insertnode2, insertnode3;
function cardGenerator() {
let data = {policies:[
{id:1, policyname: "policyname1"},
{id:2, policyname: "policyname2"},
{id:3, policyname: "policyname3"}
]}
jojo = document.getElementById("cards");
for (policy of data.policies){
insertnode2 = document.createElement("DIV");
insertnode2.innerHTML = "<ons-card>" + policy.policyname + "</ons-card>"
jojo.appendChild(insertnode2);
}
}
cardGenerator();
.intro {
text-align: center;
padding: 0 20px;
margin-top: 40px;
}
ons-card {
cursor: pointer;
color: #333;
}
.card__title,
.card--material__title {
font-size: 20px;
}
<link href="https://unpkg.com/onsenui/css/onsenui.css" rel="stylesheet"/>
<link href="https://unpkg.com/onsenui/css/onsen-css-components.css" rel="stylesheet"/>
<script src="https://unpkg.com/onsenui/js/onsenui.js"></script>
<ons-page id="policies-page">
<div id='cards'></div>
</ons-page>
Why do we need to do so: when ons-page is compiled it has divs inside. One of them is div with page__content class and it seems like we can see only elements inside this div. When you append to ons-page you append outside of div.page__content. That is why we can't see appended elements.

Categories