滚动后反应Dnd不在位置
Posted
技术标签:
【中文标题】滚动后反应Dnd不在位置【英文标题】:React Dnd out of position after scroll 【发布时间】:2019-08-20 12:56:56 【问题描述】:我正在使用react-beautiful-dnd
使表格行可拖动。
如果我从上到下拖动,则拖动工作正常,当我向上滚动页面时,它会偏离位置。
我不知道为什么。
另外,我没有发现 css 有什么奇怪的地方
我不知道为什么会这样,也不知道如何解决。 这是我的问题的一个例子。
这是我的代码:
import update from "immutability-helper";
import * as React from "react";
import DragDropContext, Draggable, Droppable from "react-beautiful-dnd";
import WithNamespaces, withNamespaces from "react-i18next";
import toastr from "react-redux-toastr";
import * as HttpHelper from "../../httpHelper";
import FormState from "../common/ValidatedForm";
type Props = WithNamespaces &
id: number;
displayName: string;
type: string;
language: any;
;
interface Fields
columns: any;
type State = FormState<Fields> &
isLoading: boolean,
canSave: boolean,
isSaving: boolean,
possibleTags: any,
configTagModalActive: boolean,
previewModalActive: boolean,
activeTag: any
;
const getItemStyle = (isDragging: any, draggableStyle: any) => (
...draggableStyle,
opacity: isDragging ? 1 : 1,
boxShadow: "0px 0px 0px 1px #8b8b8b",
);
const shadowColor = "#a0a0a057";
const Column = (props: any) =>
function findindex(val: any, pt: any)
const list = pt ? props.possibleTags : props.tags;
return list.findIndex((item: any) => val == item.name);
function findindexofhelptext(val: any, pt: any)
const list = pt;
return list.findIndex((item: any) => val == item.language);
return (
<tr ref=props.provided.innerRef ...props.provided.draggableProps style=getItemStyle(props.snapshot.isDragging, props.provided.draggableProps.style) className="draggablerow " + (props.snapshot.isDragging ? "draggedrow" : "") key=props.indexnr data-id=props.index >
<td ...props.provided.dragHandleProps style=width: "50px", textAlign: "center", cursor: "move"><i className="fa fa-bars" style=lineHeight: "40px", fontSize: "24px"></i></td>
<td style= textAlign: "center", width: "100px" >
<input
type="checkbox"
className="flipswitch"
id=props.index
checked=props.export
onChange=props.toggleVisible
/>
</td>
<td style=width: "350px" >
<input
type="text"
name="caption"
id=props.index
className="form-control"
value=props.caption
onChange=props.onTextUpdate
style=boxShadow: "2px 2px 3px 1px" + shadowColor
/>
</td>
<td style=width: "350px" >
<input
type="text"
name="fieldname"
id=props.index
className="form-control"
value=props.fieldname
onChange=props.onTextUpdate
style=boxShadow: "2px 2px 3px 1px" + shadowColor
/>
</td>
<td style=width: "400px">
<div className="tags-input" style=tagInputStyle>
Object.keys(props.tags).map((key, i) =>
<div key=i className="tag" onClick=props.onConfigButtonClicked data-id=i data-parent=props.index>
props.tags[i].name <i className="fa fa-trash" id=props.index data-key=i data-name=props.tags[i].name onClick=props.onDeleteTag style=float: "right" ></i>
</div>
)
</div>
</td>
<td style= textAlign: "center", width: "100px" >
<button onClick=() => props.onDeleteColumn(props.index) type="button" style=padding : "8px 16px", boxShadow: "2px 2px 2px 1px" + shadowColor className="btn btn-danger btn-rounded"><i className="fa fa-trash"></i></button>
</td>
</tr>
);
;
const reorder = (list: any, startIndex: any, endIndex: any) =>
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
;
interface SetColumnsResponse extends HttpHelper.ResponseData columns: any;
class CrmConnectorColumns extends React.Component<Props, State>
constructor(props: Props)
super(props);
this.moveColumn = this.moveColumn.bind(this);
this.state =
isLoading: true,
isSaving: false,
canSave: false,
errorColor: "danger",
fields: columns: [] ,
deleteModalActive: false,
configTagModalActive: false,
previewModalActive: false,
activeTag: name: "", attributes: [name: "", value: ""],
possibleTags: [
name: "PRIMARY", status: "new", helptexts: [
language: "nl", helptext: "Dit is de primary key",
language: "en", helptext: "This is the primary key"
], attributes: [], uses: 1,
name: "SUBTITLE", status: "new", helptexts: [
language: "nl", helptext: "Dit is de subtitel van een record",
language: "en", helptext: "This is The subtitle of a record"
], attributes: [], uses: 1,
name: "URL", status: "new", helptexts: [
language: "nl", helptext: "De waarde wordt gezien als link.",
language: "en", helptext: "The value becomes a link."
], attributes: [
name: "link", status: "new", helptexts: [
language: "nl", helptext: "De link krijgt deze waarde. Voorbeeld waarde is \"http://www.google.nl?search=[naam]\". de waarde van \"[naam]\" wordt ingevuld.",
language: "en", helptext: "The link gets this value. Example value is \"http://www.google.nl?search=[name]\". the value of \"[name]\" gets filled in."
]
], uses: undefined,
name: "TITLE", status: "new", helptexts: [
language: "nl", helptext: "Dit is de hoofdtitel van een record",
language: "en", helptext: "This is the maintitle of a record"
], attributes: [], uses: 1,
name: "PHONE", status: "new", helptexts: [
language: "nl", helptext: "De waarde wordt gezien als telefoonnummer",
language: "en", helptext: "The value becomes a phonenumber"
], attributes: [], uses: undefined,
name: "BUTTON", status: "new", helptexts: [
language: "nl", helptext: "Uiterlijk van een knop",
language: "en", helptext: "The value becomes a button"
], attributes: [], uses: undefined,
name: "EMAIL", status: "new", helptexts: [
language: "nl", helptext: "De waarde wordt gezien als e-mail adres",
language: "en", helptext: "The value becomes a emailaddress"
], attributes: [], uses: undefined,
name: "IMAGE", status: "new", helptexts: [
language: "nl", helptext: "De waarde wordt als afbeelding weergegeven",
language: "en", helptext: "The value gets displayed as image"
], attributes: [], uses: undefined,
name: "html", status: "new", helptexts: [
language: "nl", helptext: "De waarde wordt gezien als HTML",
language: "en", helptext: "The value gets seen as custom HTML"
], attributes: [
name: "HTML code", status: "new", helptexts: [
language: "nl", helptext: "Vul hier je custom HTML code in. De waarde tussen de [] word vervangen door de data.",
language: "en", helptext: "Enter your custom HTML here. The value between the [] will be replaced for the value."
]
], uses: undefined
]
;
this.onDragEnd = this.onDragEnd.bind(this);
onDragEnd(result: any)
// dropped outside the columns table
if (!result.destination)
return;
let newlist = [...this.state.fields.columns];
newlist = reorder(
newlist,
result.source.index,
result.destination.index
);
Object.keys(newlist).forEach((nr) =>
newlist[parseInt(nr, 10)].index = parseInt(nr, 10);
);
this.setState( fields: columns: newlist );
this.setState( canSave: true );
async componentDidMount()
console.log("Start select columns");
const fields = await HttpHelper.getJson<Fields>(`/$this.props.type/$this.props.id/columns`);
this.setState(prevState =>
return update(prevState,
fields: $set: fields ,
isLoading: $set: false ,
);
);
if (this.state.fields.columns == undefined)
this.setState( fields: columns: [] );
for (let i = 0; i < fields.columns.length; i++)
fields.columns[i].index = i;
this.setState( fields: columns: fields.columns );
const newlist = [...this.state.possibleTags];
for (const column of fields.columns)
for (const tags of column.tags)
const index = newlist.map((item) => item.name).indexOf(tags.name);
if (newlist[index].uses > 0)
newlist[index].uses = 0;
this.setState( possibleTags: newlist );
moveColumn(index: any, indexnr: any)
const cards = this.state.fields.columns;
const sourceCard = cards.find((card: any) => card.index === index);
const sortCards = cards.filter((card: any) => card.index !== index);
sortCards.splice(indexnr, 0, sourceCard);
Object.keys(sortCards).forEach((nr) =>
sortCards[nr].index = parseInt(nr, 10);
);
this.setState( fields: columns: sortCards );
this.setState( canSave: true );
onDragStart = (e: any) =>
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData("text/html", e.target.parentNode);
e.dataTransfer.setDragImage(e.target.parentNode, 20, 20);
ondragOver(e: any)
e.preventDefault();
public render()
const columns = this.state.fields.columns || [] ;
const t = this.props;
let placeholder: any;
if (columns.length < 1)
placeholder = <tr style=boxShadow: "0px 0px 0px 1px #8b8b8b", textAlign: "center" className="draggablerow"><td colSpan=6 >t("placeholder")</td></tr>;
return (
<form>
<div className="App">
<main>
<button onClick=this.onSubmit className="btn btn-primary" type="submit" style=float: "right", boxShadow: "2px 2px 3px 1px" + shadowColor disabled=!this.state.canSave || this.state.isSaving>this.state.isSaving ? <i className="fa fa-spinner fa-spin"></i> : "" this.props.t("update")</button>
<button onClick=this.onPreviewButtonClicked type="button" className="btn btn-primary" style=float: "right", boxShadow: "2px 2px 3px 1px" + shadowColor, marginRight: "5px" >Preview</button><br/><br/>
<DragDropContext onDragEnd=this.onDragEnd>
<table className="col-8 table columns" style=tableLayout: "auto" >
<thead className="" style=border: "2px solid #1b2847", background: "#1b2847", color: "white">
<tr>
<th colSpan=2 style=textAlign: "center">
<button onClick=this.onAddColumn disabled=columns.length > 14 ? true : false type="button" style=padding : "8px 16px", boxShadow: "2px 2px 3px 1px" + shadowColor className="btn btn-primary btn-rounded"><i className="fa fa-plus"></i> </button>
</th>
<th>t("displayname")</th>
<th>Element</th>
<th>Tags</th>
<th></th>
</tr>
</thead>
<Droppable droppableId="droppable" direction="vertical">
(provided: any) => (
<tbody ref=provided.innerRef>
Object.keys(columns).map((element, key) => (
<Draggable key="draggable" + key draggableId=element index=key>
(provided, snapshot) => (
<Column
key="column" + key
indexnr=key
toggleVisible=this.toggleVisible
onTextUpdate=this.onTextUpdate
onDeleteColumn=this.onDeleteColumn
onDeleteTag=this.onDeleteTag
onAddTag=this.onAddTag
possibleTags=this.state.possibleTags
onConfigButtonClicked=this.onConfigButtonClicked
onPreviewButtonClicked=this.onPreviewButtonClicked
onClosePreview=this.onClosePreview
provided=provided
snapshot=snapshot
language=this.props.language
...columns[key]
/>
)
</Draggable>
))
provided.placeholder
</tbody>
)
</Droppable>
</table>
</DragDropContext>
</main>
</div>
</form>
);
export default withNamespaces(["crmConnectorColumns", "Common"])(CrmConnectorColumns);
我希望有人能找出为什么当我在页面上向下滚动时我的可拖动对象会错位。
【问题讨论】:
你能在codesandbox中添加一个静态数据的例子吗?我尝试添加一个示例,但代码中缺少导入的文件。 我在这里创建了一个非常基本的示例,其中包含您的代码核心:codesandbox.io/s/my64446wl9 我没有看到与您相同的结果,所以我敢打赌它是在样式被覆盖某处。 浏览器版本不同?如果您得到相同的结果,也可以在隐身模式下尝试(有时扩展程序会干扰页面) 可能是您的可拖动元素上有一个溢出scoll。也许尝试设置overflow-y:隐藏在可拖动元素上 所以 onscroll 一些样式被覆盖了? 【参考方案1】:也许答案为时已晚,但对于某些人来说,它可能会有所帮助。
如果您仔细观察,您会在滚动时看到偏移量,这就是样式损坏的原因。
对于解决方案,您应该考虑滚动容器,如果您将滚动附加到 HTMLElement,而不是 Window,则需要检查此example。
此问题与react-beautiful-dnd
本身有关,更新版本将解决该问题。
【讨论】:
【参考方案2】:我遇到了同样的问题。对我来说,问题是 droppable(list) 在里面,例如,main 容器是可滚动的(即溢出:滚动)。
我通过将 droppable 转换为可滚动而不是 main container
解决了这个问题有问题的示例
.main
background: #eee;
padding: 3rem;
height: 200px;
overflow-y: scroll;
.droppable
padding: 1rem;
background: #aaa;
.draggable
margin: 0.5rem 0;
padding: 1rem;
background: #ccc;
<div class="main">
<div class="droppable">
<div class="draggable">
<span class="text"> item</span>
</div>
<div class="draggable">
<span class="text"> item</span>
</div>
<div class="draggable">
<span class="text"> item</span>
</div>
</div>
</div>
已解决问题的示例
.main
background: #eee;
padding: 3rem;
height: 200px;
.droppable
padding: 1rem;
background: #aaa;
height: 180px;
overflow-y: scroll;
.draggable
margin: 0.5rem 0;
padding: 1rem;
background: #ccc;
<div class="main">
<div class="droppable">
<div class="draggable">
<span class="text"> item</span>
</div>
<div class="draggable">
<span class="text"> item</span>
</div>
<div class="draggable">
<span class="text"> item</span>
</div>
</div>
</div>
仅在 CSS 中进行了更改,以使 droppable 容器比 main 容器更短,并将 overvlow-y:scroll
添加到 droppable
【讨论】:
【参考方案3】:我也遇到了同样的问题。
我发现的唯一解决方法是
*在垂直拖动的情况下固定项目的高度。 *在水平拖动的情况下固定项目的宽度。
还有一件事要提到的是设置拖动到块的项目的显示属性。
【讨论】:
此处提供所需 CSS 的代码示例以上是关于滚动后反应Dnd不在位置的主要内容,如果未能解决你的问题,请参考以下文章