Почему срабатывает pointerleave во время события pointermove на touch устройствах? Слайдер
Пишу свой слайдер на JS. Появилась такая проблема - событие pointerleave на touch устройствах срабатывает во время перетаскивания элементов слайдера и останавливает перетаскивание. Не понимаю как с этим бороться, мне нужно отключить такое поведение
class Slider {
#nodes = {
sliderNode: null,
sliderItemsNode: null,
sliderItemNodes: [],
sliderArrowLeftNode: null,
sliderArrowRightNode: null,
sliderWrapperNode: null
};
#cssSelectors = {
items: '.slider__items',
item: '.slider__item',
wrapper: '.slider__wrapper',
arrowLeft: '.slider__arrow_left',
arrowRight: '.slider__arrow_right'
};
#centeringShiftX = 0;
#changeSlideShiftX = 0;
#moveSlideShiftX = 0;
#shiftX = 0;
#lastShifX = 0;
#startMovePos = {
x: 0,
y: 0
};
#activeItemIndex = 0;
#isDragging = false;
#countDragging = 0;
#teamsTitleNode = document.querySelector('.teams__title');
constructor(sliderSelector) {
this.#initNodes(sliderSelector);
this.#initEventListeners();
this.#calcAndSetShiftX();
}
#initNodes(sliderSelector) {
this.#nodes.sliderNode = document.querySelector(sliderSelector);
if (this.#nodes.sliderNode === null) {
throw new Error(`Slider: по селектору ${sliderSelector} не найден элемент в DOM дереве`);
}
this.#nodes.sliderItemsNode = this.#nodes.sliderNode.querySelector(this.#cssSelectors.items);
if (this.#nodes.sliderItemsNode === null) {
throw new Error(`Slider: по селектору ${this.#cssSelectors.items} не найден элемент в DOM дереве`);
}
this.#nodes.sliderItemNodes = Array.from(this.#nodes.sliderNode.querySelectorAll(this.#cssSelectors.item));
if (this.#nodes.sliderItemNodes.length === 0) {
throw new Error(`Slider: по селектору ${this.#cssSelectors.item} не найдены элементы слайдера в DOM дереве`);
}
this.#nodes.sliderArrowLeftNode = this.#nodes.sliderNode.querySelector(this.#cssSelectors.arrowLeft);
if (this.#nodes.sliderArrowLeftNode === null) {
throw new Error(`Slider: по селектору ${this.#cssSelectors.arrowLeft} не найден элемент в DOM дереве`);
}
this.#nodes.sliderArrowRightNode = this.#nodes.sliderNode.querySelector(this.#cssSelectors.arrowRight);
if (this.#nodes.sliderArrowLeftNode === null) {
throw new Error(`Slider: по селектору ${this.#cssSelectors.arrowRight} не найден элемент в DOM дереве`);
}
this.#nodes.sliderWrapperNode = this.#nodes.sliderNode.querySelector(this.#cssSelectors.wrapper);
if (this.#nodes.sliderWrapperNode === null) {
throw new Error(`Slider: по селектору ${this.#cssSelectors.wrapper} не найден элемент в DOM дереве`);
}
}
#initEventListeners() {
window.addEventListener('resize', this.#debounce(this.#calcAndSetShiftX, 50));
this.#nodes.sliderArrowLeftNode.addEventListener('click', () => {
const nextIndex = this.#getNextIndex(-1);
this.#changeSlide(nextIndex);
});
this.#nodes.sliderArrowRightNode.addEventListener('click', () => {
const nextIndex = this.#getNextIndex(1);
this.#changeSlide(nextIndex);
});
for (const sliderNode of this.#nodes.sliderItemNodes) {
sliderNode.addEventListener('pointerdown', this.#dragStart);
}
document.addEventListener('pointermove', this.#dragging);
// document.addEventListener('touchmove', this.#dragging);
document.addEventListener('pointerup', this.#dragStop);
document.addEventListener('pointerleave', (event) => {
console.log('pointerleave');
this.#dragStop();
});
}
#dragStart = (e) => {
if (e.button === 2 || e.button === 1) return false; // Если это правая или средняя кнопка мыши, это не тот клик
this.#isDragging = true;
this.#startMovePos = { x: e.clientX, y: e.clientY };
this.#lastShifX = this.#shiftX;
console.log('dragStart');
}
#dragStop = (e) => {
this.#isDragging = false;
if (this.#moveSlideShiftX === 0) return;
const dir = this.#moveSlideShiftX < 0 ? 'left' : 'right';
// console.log(this.#moveSlideShiftX, 'this.#moveSlideShiftX');
// console.log(dir);
const currentSliderNode = this.#nodes.sliderItemNodes[this.#activeItemIndex];
// console.log(currentSliderNode);
if (dir === 'left') {
}
this.#moveSlideShiftX = 0;
this.#calcAndSetShiftX();
console.log('dragStop');
}
#dragging = (e) => {
if (this.#isDragging === false) return;
const nextMovePos = { x: e.clientX, y: e.clientY };
const diffMovePos = { x: nextMovePos.x - this.#startMovePos.x, y: nextMovePos.y - this.#startMovePos.y };
this.#moveSlideShiftX = diffMovePos.x;
this.#calcMoveSlideShiftX();
this.#countDragging++;
this.#teamsTitleNode.innerText = `${this.#countDragging} dragging`;
console.log('dragging');
}
#getNextIndex(dir) {
const quantityItems = this.#nodes.sliderItemNodes.length;
return (this.#activeItemIndex + dir + quantityItems) % quantityItems;
}
#setCenteringShiftX = () => {
const firstSliderItemNode = this.#nodes.sliderItemNodes[this.#activeItemIndex];
const sliderItemsNode = this.#nodes.sliderItemsNode;
const widthSlider = sliderItemsNode.offsetWidth;
const widthFirstSliderItem = firstSliderItemNode.offsetWidth;
this.#centeringShiftX = Math.max((widthSlider - widthFirstSliderItem) / 2, 0);
}
#setChangeSlideShiftX() {
const firstRect = this.#nodes.sliderItemNodes[0].getBoundingClientRect();
const nextRect = this.#nodes.sliderItemNodes[this.#activeItemIndex].getBoundingClientRect();
const diffLeftNextSlide = nextRect.left - firstRect.left;
this.#changeSlideShiftX = diffLeftNextSlide;
}
#setShiftX() {
this.#shiftX = this.#centeringShiftX - this.#changeSlideShiftX;
this.#nodes.sliderItemsNode.style.transform = `translateX(${this.#shiftX}px)`;
}
#calcAndSetShiftX = () => {
this.#setCenteringShiftX();
this.#setChangeSlideShiftX();
this.#setShiftX();
}
#calcMoveSlideShiftX() {
this.#shiftX = this.#lastShifX + this.#moveSlideShiftX;
this.#nodes.sliderItemsNode.style.transform = `translateX(${this.#shiftX}px)`;
}
#changeSlide(nextIndex) {
this.#activeItemIndex = nextIndex;
this.#calcAndSetShiftX();
}
#debounce(func, delay) {
let timeoutId;
return function(...args) {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
#throttle(func, ms) {
let isThrottled = false,
savedArgs,
savedThis;
function wrapper() {
if (isThrottled) { // (2)
savedArgs = arguments;
savedThis = this;
return;
}
func.apply(this, arguments); // (1)
isThrottled = true;
setTimeout(function() {
isThrottled = false; // (3)
if (savedArgs) {
wrapper.apply(savedThis, savedArgs);
savedArgs = savedThis = null;
}
}, ms);
}
return wrapper;
}
}
const slider = new Slider('.teams__slider');
*,
*::before,
*::after {
box-sizing: inherit;
}
:root {
--maxWidthContainer: 1094px;
}
html {
box-sizing: border-box;
}
body {
margin: 0;
font-size: 16px;
font-family: 'Roboto', sans-serif;
}
img {
display: block;
max-width: 100%;
max-height: 100%;
}
.container {
max-width: var(--maxWidthContainer);
display: block;
margin: 0 auto;
padding: 0 1em;
}
.slider {
display: flex;
flex-direction: column;
gap: 32px;
user-select: none;
}
.slider__arrows {
display: flex;
gap: 30px;
margin: 0 auto;
user-select: none;
}
.slider__items {
display: flex;
align-items: center;
gap: 50px;
user-select: none;
}
.slider__wrapper img {
pointer-events: none;
}
.slider__item {
cursor: grab;
}
.slider__arrow {
cursor: pointer;
padding: 20px;
transition: background-color 0.2s ease;
}
.slider__arrow:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.teams {
background-color: rgb(39, 55, 32);
padding: 120px 0;
overflow-x: hidden;
}
.teams__title {
color: white;
margin: 0;
font-size: 4.5em;
text-align: center;
}
.teams__wrapper {
display: flex;
flex-direction: column;
gap: 57px;
}
.teams__slider-item {
padding: 44px 82px;
display: flex;
flex-direction: column;
align-items: center;
gap: 30px;
border-radius: 30px;
--width: max(75%, 500px);
min-width: var(--width);
max-width: var(--width);
}
.teams__slider-item:nth-child(2n + 1) {
/* нечётные */
background-color: white;
}
.teams__slider-item:nth-child(2n) {
/* чётные */
background-color: #D2EBD5;
}
.teams__slider-item-img {
--width: 128px;
min-width: var(--width);
max-width: var(--width);
}
.teams__slider-text {
margin: 0;
font-size: 2em;
}
.teams__slider-review {
display: flex;
align-items: center;
gap: 24px;
}
.teams__slider-review-img {
--width: 64px;
min-width: var(--width);
max-width: var(--width);
}
.teams__slider-review-text {
font-size: 1.125em;
}
.as-console {
display: none;
visibility: hidden;
opacity: 0;
position: fixed;
z-index: -1;
}
<section class="teams">
<div class="container">
<div class="teams__wrapper">
<h2 class="teams__title">Trusted by top teams</h2>
<div class="teams__slider slider">
<div class="slider__wrapper">
<div class="slider__items teams__slider-items">
<div class="slider__item teams__slider-item">
<div class="teams__slider-item-img-wrapper">
<img src="https://picsum.photos/100" alt="" class="teams__slider-item-img">
</div>
<p class="teams__slider-text">
“HyperComply has enabled us to complete questionnaires quickly and accurately, and has had a hugely positive effect on speeding up our sales cycle”
</p>
<div class="teams__slider-review">
<div class="teams__slider-review-img-wrapper">
<img src="https://picsum.photos/50" alt="" class="teams__slider-review-img">
</div>
<div class="teams__slider-review-text">
Desiree R, Sr. Director of Information Security
</div>
</div>
</div>
<!-- .slider__item.teams__slider-item -->
<div class="slider__item teams__slider-item">
<div class="teams__slider-item-img-wrapper">
<img src="https://picsum.photos/100" alt="" class="teams__slider-item-img">
</div>
<p class="teams__slider-text">
“With HyperComply, we can now complete questionnaires with ease and accuracy, resulting in a faster sales cycle. This innovative tool has truly transformed the way we do business”
</p>
<div class="teams__slider-review">
<div class="teams__slider-review-img-wrapper">
<img src="https://picsum.photos/50" alt="" class="teams__slider-review-img">
</div>
<div class="teams__slider-review-text">
Desiree R, Sr. Director of Information Security
</div>
</div>
</div>
<!-- .slider__item.teams__slider-item -->
<div class="slider__item teams__slider-item">
<div class="teams__slider-item-img-wrapper">
<img src="https://picsum.photos/100" alt="" class="teams__slider-item-img">
</div>
<p class="teams__slider-text">
“Thanks to HyperComply, we are able to breeze through questionnaires and speed up our sales cycle. This game-changing technology has made a significant impact on our business operations.”
</p>
<div class="teams__slider-review">
<div class="teams__slider-review-img-wrapper">
<img src="https://picsum.photos/50" alt="" class="teams__slider-review-img">
</div>
<div class="teams__slider-review-text">
Desiree R, Sr. Director of Information Security
</div>
</div>
</div>
</div>
</div>
<div class="slider__arrows">
<div class="slider__arrow slider__arrow_left">
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="560 120 583 1000" id="svg3001" version="1.1" inkscape:version="0.48.3.1 r9886" width="20px" height="auto" sodipodi:docname="angle_left_font_awesome.svg">
<metadata id="metadata3011">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<defs id="defs3009"/>
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview3007" showgrid="false" inkscape:zoom="0.13169643" inkscape:cx="896" inkscape:cy="896" inkscape:window-x="0" inkscape:window-y="25" inkscape:window-maximized="0" inkscape:current-layer="svg3001"/>
<g transform="matrix(1,0,0,-1,516.33898,1194.3051)" id="g3003">
<path fill="white" d="m 627,992 q 0,-13 -10,-23 L 224,576 617,183 q 10,-10 10,-23 0,-13 -10,-23 L 567,87 Q 557,77 544,77 531,77 521,87 L 55,553 q -10,10 -10,23 0,13 10,23 l 466,466 q 10,10 23,10 13,0 23,-10 l 50,-50 q 10,-10 10,-23 z" id="path3005" inkscape:connector-curvature="0"/>
</g>
</svg>
</div>
<div class="slider__arrow slider__arrow_right">
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="560 120 583 1000" id="svg3001" version="1.1" inkscape:version="0.48.3.1 r9886" width="20px" height="auto" style="transform: scaleX(-1)" sodipodi:docname="angle_left_font_awesome.svg">
<metadata id="metadata3011">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<defs id="defs3009"/>
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview3007" showgrid="false" inkscape:zoom="0.13169643" inkscape:cx="896" inkscape:cy="896" inkscape:window-x="0" inkscape:window-y="25" inkscape:window-maximized="0" inkscape:current-layer="svg3001"/>
<g transform="matrix(1,0,0,-1,516.33898,1194.3051)" id="g3003">
<path fill="white" d="m 627,992 q 0,-13 -10,-23 L 224,576 617,183 q 10,-10 10,-23 0,-13 -10,-23 L 567,87 Q 557,77 544,77 531,77 521,87 L 55,553 q -10,10 -10,23 0,13 10,23 l 466,466 q 10,10 23,10 13,0 23,-10 l 50,-50 q 10,-10 10,-23 z" id="path3005" inkscape:connector-curvature="0"/>
</g>
</svg>
</div>
</div>
</div>
</div>
</section>
Источник: Stack Overflow на русском