Skip to content

Commit afb2ac2

Browse files
committed
fixed bug #464
1 parent 0c31d0b commit afb2ac2

File tree

4 files changed

+160
-24
lines changed

4 files changed

+160
-24
lines changed

packages/src/utils/dom.js

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function hasClass(el, cls) {
6161
}
6262
}
6363

64-
/*获取当前元素的left、top偏移
64+
/*获取当前元素的偏移(相对于整个document)
6565
* offsetTop:元素最顶端距离文档顶端的距离,包含滚动条
6666
* offsetleft:元素最左侧距离文档左侧的距离,包含滚动条
6767
* left:元素最左侧距离文档左侧的距离,不包含滚动条
@@ -71,11 +71,11 @@ export function hasClass(el, cls) {
7171
* right2:元素最左侧距离文档右侧的距离,不包含滚动条
7272
* bottom2:元素最底端距离文档最底部的距离,不包含滚动条
7373
* */
74-
export function getViewportOffset(element) {
74+
export function getViewportOffset(triggerEl) {
7575
var doc = document.documentElement,
7676
box =
77-
typeof element.getBoundingClientRect !== "undefined"
78-
? element.getBoundingClientRect()
77+
typeof triggerEl.getBoundingClientRect !== "undefined"
78+
? triggerEl.getBoundingClientRect()
7979
: 0,
8080
scrollLeft =
8181
(window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0),
@@ -99,6 +99,51 @@ export function getViewportOffset(element) {
9999
};
100100
}
101101

102+
/*获取当前元素的偏移(相对于外层容器)
103+
* offsetTop:元素最顶端距离文档顶端的距离,包含滚动条
104+
* offsetleft:元素最左侧距离文档左侧的距离,包含滚动条
105+
* left:元素最左侧距离文档左侧的距离,不包含滚动条
106+
* top:元素最顶端距离文档顶端的距离,不包含滚动条
107+
* right:元素最右侧距离文档右侧的距离,不包含滚动条
108+
* bottom:元素最底端距离文档底端的距离,不包含滚动条
109+
* right2:元素最左侧距离文档右侧的距离,不包含滚动条
110+
* bottom2:元素最底端距离文档最底部的距离,不包含滚动条
111+
* */
112+
export function getViewportOffsetWithinContainer(triggerEl, containerEl) {
113+
const {
114+
offsetTop: tElOffsetTop,
115+
offsetLeft: tElOffsetLeft,
116+
left: tElLef,
117+
top: tElTop,
118+
right: tElRight,
119+
bottom: tElBottom,
120+
right2: tElRight2,
121+
bottom2: tElBottom2,
122+
} = getViewportOffset(triggerEl);
123+
124+
const {
125+
offsetTop: cElOffsetTop,
126+
offsetLeft: cElOffsetLeft,
127+
left: cElLef,
128+
top: cElTop,
129+
right: cElRight,
130+
bottom: cElBottom,
131+
right2: cElRight2,
132+
bottom2: cElBottom2,
133+
} = getViewportOffset(containerEl);
134+
135+
return {
136+
offsetTop: tElOffsetTop - cElOffsetTop,
137+
offsetLeft: tElOffsetLeft - cElOffsetLeft,
138+
left: tElLef - cElLef,
139+
top: tElTop - cElTop,
140+
right: tElRight - cElRight,
141+
bottom: tElBottom - cElBottom,
142+
right2: tElRight2 - cElRight2,
143+
bottom2: tElBottom2 - cElBottom2,
144+
};
145+
}
146+
102147
/*获取鼠标相对于文档的坐标
103148
* left:鼠标点击位置距离文档左侧的距离,包含滚动条
104149
* top: 鼠标点击位置距离文档顶端的距离,包含滚动条

packages/ve-dropdown/src/index.jsx

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import VeRadio from "vue-easytable/packages/ve-radio";
44
import { COMPS_NAME, EMIT_EVENTS } from "./util/constant";
55
import { clsName } from "./util/index";
66
import { getRandomId } from "../../src/utils/random";
7-
import { getViewportOffset } from "../../src/utils/dom";
7+
import {
8+
getViewportOffset,
9+
getViewportOffsetWithinContainer,
10+
} from "../../src/utils/dom";
811

912
export default {
1013
name: COMPS_NAME.VE_DROPDOWN,
@@ -101,6 +104,13 @@ export default {
101104
type: Number,
102105
default: 5,
103106
},
107+
// popper append to element
108+
popperAppendTo: {
109+
type: [String, HTMLElement],
110+
default: function () {
111+
return document.body;
112+
},
113+
},
104114
},
105115
data() {
106116
return {
@@ -113,6 +123,10 @@ export default {
113123
rootId: "",
114124
// dropdown items panel id
115125
dropdownItemsPanelId: "",
126+
// 弹出被添加到的目标元素
127+
popperAppendToEl: null,
128+
// 弹出被添加到的目标元素标签名称
129+
appendToElTagName: null,
116130
};
117131
},
118132
computed: {
@@ -250,7 +264,12 @@ export default {
250264

251265
// change dropdown panel position
252266
changDropdownPanelPosition() {
253-
const { defaultInstance, rootId } = this;
267+
const {
268+
defaultInstance,
269+
rootId,
270+
popperAppendToEl,
271+
appendToElTagName,
272+
} = this;
254273

255274
let rootEl = document.querySelector(`#${rootId}`);
256275

@@ -261,40 +280,61 @@ export default {
261280
const triggerEl = this.$el.querySelector(".ve-dropdown-dt");
262281
const { height: triggerElHeight } =
263282
triggerEl.getBoundingClientRect();
264-
// const {
265-
// left: triggerElLeft,
266-
// top: triggerElTop,
267-
// right: triggerElRight,
268-
// bottom: triggerElBottom,
269-
// } = getMousePosition(event);
283+
284+
if (!popperAppendToEl) {
285+
return false;
286+
}
287+
288+
// is append to body
289+
const isAppendToBody = appendToElTagName === "BODY";
270290

271291
const {
272292
offsetLeft: triggerElLeft,
273293
offsetTop: triggerElTop,
274294
right: triggerElRight,
275295
bottom: triggerElBottom,
276-
} = getViewportOffset(triggerEl);
296+
} = isAppendToBody
297+
? getViewportOffset(triggerEl)
298+
: getViewportOffsetWithinContainer(
299+
triggerEl,
300+
popperAppendToEl,
301+
);
277302

278303
let panelX = 0;
279304
let panelY = 0;
280305

306+
// 如果不是添加到body 需要考虑外层容器滚动调的影响
307+
let scrollLeft = 0;
308+
let scrollTop = 0;
309+
if (!isAppendToBody) {
310+
scrollLeft = popperAppendToEl.scrollLeft;
311+
scrollTop = popperAppendToEl.scrollTop;
312+
}
313+
281314
// 右方宽度够显示
282315
if (triggerElRight >= currentPanelWidth) {
283-
panelX = triggerElLeft;
316+
panelX = triggerElLeft + scrollLeft;
284317
}
285318
// 右方宽度不够显示在鼠标点击左方
286319
else {
287-
panelX = triggerElLeft - currentPanelWidth;
320+
panelX = triggerElLeft - currentPanelWidth + scrollLeft;
288321
}
289322

290323
// 下方高度够显示
291324
if (triggerElBottom >= currentPanelHeight) {
292-
panelY = triggerElTop + triggerElHeight + defaultInstance;
325+
panelY =
326+
triggerElTop +
327+
triggerElHeight +
328+
defaultInstance +
329+
scrollTop;
293330
}
294331
// 下方高度不够显示在鼠标点击上方
295332
else {
296333
panelY =
297-
triggerElTop - currentPanelHeight - defaultInstance;
334+
triggerElTop -
335+
currentPanelHeight -
336+
defaultInstance +
337+
scrollTop;
298338
}
299339

300340
rootEl.style.left = panelX + "px";
@@ -386,8 +426,13 @@ export default {
386426
return clsName(getRandomId());
387427
},
388428

389-
// add root contextmenu panel to body
390-
addRootElementToBody() {
429+
/*
430+
add root contextmenu panel to element
431+
如果不指定则添加到 body
432+
*/
433+
addRootElementToElement() {
434+
const { popperAppendTo } = this;
435+
391436
this.rootId = this.getRandomIdWithPrefix();
392437
this.dropdownItemsPanelId = this.getRandomIdWithPrefix();
393438

@@ -402,7 +447,19 @@ export default {
402447

403448
containerEl.setAttribute("id", this.rootId);
404449

405-
document.body.appendChild(containerEl);
450+
if (
451+
typeof popperAppendTo === "string" &&
452+
popperAppendTo.length > 0
453+
) {
454+
this.popperAppendToEl =
455+
document.querySelector(popperAppendTo);
456+
} else {
457+
this.popperAppendToEl = popperAppendTo;
458+
}
459+
460+
this.appendToElTagName = this.popperAppendToEl.tagName;
461+
462+
this.popperAppendToEl.appendChild(containerEl);
406463
});
407464
}
408465
},
@@ -412,16 +469,34 @@ export default {
412469
this.init();
413470
},
414471
mounted() {
415-
this.addRootElementToBody();
416-
417-
document.addEventListener("scroll", this.changDropdownPanelPosition);
472+
this.addRootElementToElement();
473+
474+
this.$nextTick(() => {
475+
const targetEl =
476+
this.appendToElTagName === "BODY"
477+
? document
478+
: this.popperAppendToEl;
479+
targetEl.addEventListener(
480+
"scroll",
481+
this.changDropdownPanelPosition,
482+
);
483+
});
418484
window.addEventListener("resize", this.changDropdownPanelPosition);
419485
},
420486

421487
destroyed() {
422488
this.removeOrEmptyRootPanel();
423489

424-
document.removeEventListener("scroll", this.changDropdownPanelPosition);
490+
this.$nextTick(() => {
491+
const targetEl =
492+
this.appendToElTagName === "BODY"
493+
? document
494+
: this.popperAppendToEl;
495+
targetEl.removeEventListener(
496+
"scroll",
497+
this.changDropdownPanelPosition,
498+
);
499+
});
425500
window.removeEventListener("resize", this.changDropdownPanelPosition);
426501
},
427502

packages/ve-pagination/src/index.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export default {
7272
<VeSelect
7373
class={clsName("select")}
7474
value={this.$parent.newPageSizeOption}
75+
popperAppendTo={this.$parent.popperAppendTo}
7576
on-input={this.handleChange}
7677
//v-model={this.$parent.newPageSizeOption}
7778
/>
@@ -154,6 +155,13 @@ export default {
154155
return [10, 20, 30];
155156
},
156157
},
158+
// popper append to element
159+
popperAppendTo: {
160+
type: [String, HTMLElement],
161+
default: function () {
162+
return document.body;
163+
},
164+
},
157165
},
158166
data() {
159167
return {

packages/ve-select/src/index.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ export default {
5050
type: Boolean,
5151
default: false,
5252
},
53+
// popper append to element
54+
popperAppendTo: {
55+
type: [String, HTMLElement],
56+
default: function () {
57+
return document.body;
58+
},
59+
},
5360
},
5461
data() {
5562
return {
@@ -133,6 +140,7 @@ export default {
133140
// v-model
134141
value: this.internalOptions,
135142
hideByItemClick: true,
143+
popperAppendTo: this.popperAppendTo,
136144
},
137145
style: {
138146
width: this.width,

0 commit comments

Comments
 (0)