Глобальное переопределение курсора мыши с помощью JavaScript

В своем веб-приложении я пытаюсь реализовать некоторые функции перетаскивания. У меня есть глобальный компонент JavaScript, который выполняет основные функции. Этот объект также отвечает за изменение курсора мыши в зависимости от текущей операции перетаскивания (перемещение, копирование, связывание). На моей веб-странице есть различные элементы HTML, которые определяют собственный стиль курсора, либо встроенный, либо через файл CSS.

Итак, есть ли способ для моего центрального компонента перетаскивания изменить курсор мыши глобально, независимо от стиля элемента, над которым находится курсор мыши?

Я пытался:

document.body.style.cursor = "move"

и

document.body.style.cursor = "move !important"

Но это не работает. Каждый раз, когда я перетаскиваю элемент, который определяет стиль курсора, курсор меняется на этот стиль.

Конечно, я мог бы изменить стиль элемента, который я сейчас перетаскиваю, но тогда мне придется сбросить его, когда я покину элемент. Это кажется немного сложным. Я ищу глобальное решение.


person n.dee    schedule 25.05.2012    source источник


Ответы (6)


Пожалуйста, не уничтожайте свой CSS!

Чтобы реализовать функцию перетаскивания, вы должны использовать очень важный API: element.setCapture(), который делает следующее:

  • Все события мыши перенаправляются на целевой элемент захвата, независимо от того, где они произошли (даже за пределами окна браузера).
  • Курсор будет курсором целевого элемента захвата, независимо от того, где находится указатель мыши.

Вы должны вызвать element.releaseCapture() или document.releaseCapture(), чтобы вернуться в нормальный режим в конце операции.

Остерегайтесь наивной реализации перетаскивания: у вас может возникнуть множество болезненных вопросов, например (среди прочего): что произойдет, если отпустить мышь за пределами окно браузера или над элементом, у которого есть обработчик, останавливающий распространение. Использование setCapture() решает все эти проблемы, а также стиль курсора.

Если вы хотите внедрить перетащите себя.

Возможно, вы также могли бы использовать перетаскиваемый пользовательский интерфейс jQuery, если это возможно в вашем контексте.

person Samuel Rossille    schedule 25.05.2012
comment
setCapture работает в IE и FF/Moz, но, похоже, Chrome не поддерживает эту замечательную функцию :( - person eselk; 29.01.2013
comment
setCapture сегодня работает только в Firefox (не в IE 11). Но его можно использовать, когда он есть. - person ygoe; 22.12.2017
comment
2021 (другая сторона после 2020), документы Mozilla иллюстрируют разработчика. mozilla.org/en-US/docs/Web/API/Element/setCapture: эта функция не является стандартной и не соответствует стандартам. Не используйте его на рабочих сайтах, выходящих в Интернет: он не будет работать для каждого пользователя. Также могут быть большие несовместимости между реализациями, и поведение может измениться в будущем. - person Mohab Khaled; 20.01.2021

document.body.style.cursor = "move"

должно работать нормально.

Тем не менее, я рекомендую делать глобальную стилизацию через CSS.

определить следующее:

body{
   cursor:move;
}

Проблема в том, что определенные курсоры на других элементах переопределяют стиль тела.

Вы можете сделать что-то вроде этого:

your-element.style.cursor = "inherit"; // (or "default")

чтобы сбросить его к унаследованному стилю из тела или с помощью CSS:

body *{
   cursor:inherit;
}

Обратите внимание, однако, что * обычно считается плохим выбором селектора.

person Christoph    schedule 25.05.2012

К сожалению, element.setCapture() не работает для IE

Я использую подход грубой силы - открываю прозрачный div поверх всей страницы на время перетаскивания.

.tbFiller {
   position:absolute;
   z-index:5000; 
   left:0;
   top:0;
   width:100%;
   height:100%; 
   background-color:transparent;
   cursor:move;
}

...

function dragStart(event) {
    // other code...
    document.tbFiller=document.createElement("div");
    document.tbFiller.className="tbFiller"
}

function dragStop(event) {
    // other code...
    document.tbFiller.parentNode.removeChild(document.tbFiller);
}
person Andrey Subbotin    schedule 26.06.2012
comment
setCapture работает в IE, но не думаю, что в Chrome есть что-то подобное - person eselk; 29.01.2013
comment
setCapture не работает в IE 11. Firefox говорит, что это Microsoft API, возможно, они снова его удалили. Но вам не нужен оверлейный div, достаточно правила CSS и установки класса. - person ygoe; 22.12.2017

Это то, что я делаю, и это работает в Firefox, Chrome, Edge и IE с 2017 года.

Добавьте это правило CSS на свою страницу:

html.reset-all-cursors *
{
    cursor: inherit !important;
}

Когда элемент <html> имеет класс «reset-all-cursors», он переопределяет все курсоры, которые установлены для элементов индивидуально в их атрибуте style, без фактического манипулирования самими элементами. Не нужно убирать везде.

Затем, когда вы хотите переопределить свой курсор на всей странице курсором любого element, например. г. перетаскиваемый элемент, сделайте это в JavaScript:

element.setCapture && element.setCapture();
$("html").addClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", $(element).css("cursor"), "important");

Он использует функцию setCapture там, где она доступна. В настоящее время это просто Firefox, хотя они говорят, что это Microsoft API. Затем ко всему документу добавляется специальный класс, который отключает все пользовательские курсоры. Наконец, установите нужный курсор в документе, чтобы он отображался везде.

В сочетании с захватом событий это может даже вывести курсор перетаскивания за пределы страницы и окна браузера. setCapture надежно делает это в Firefox. Но в других местах это не работает каждый раз, в зависимости от браузера, его макета пользовательского интерфейса и пути, по которому курсор мыши покидает окно. ;-)

Когда вы закончите, очистите:

element.releaseCapture && element.releaseCapture();
$("html").removeClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", "");

Это включает jQuery для addClass и removeClass. В простых сценариях вы можете просто сравнить и установить атрибут class для document.documentElement. Однако это сломает другие библиотеки, такие как Modernizr. Вы можете избавиться от функции css, если вы уже знаете желаемый курсор элемента, или попробуйте что-то вроде element.style.cursor.

person ygoe    schedule 22.12.2017
comment
Это не очень хорошо, потому что изменение класса reset-all-cursors приведет к тому, что каждый элемент на странице будет переоценивать свой стиль, что является медленным и приводит к задержке любой анимации и т. д. на странице. - person Glenn Maynard; 03.10.2018
comment
Проблем с ним пока не наблюдал. И я не понимаю заминки анимации. По крайней мере, в моем случае никакие анимации не ведут себя как-то заметно. - person ygoe; 03.10.2018
comment
Для тех, кто не использует jQuery: developer.mozilla.org/en- США/документы/Интернет/API/Документ/ - person Andrew; 22.07.2020

Благодаря некоторым другим ответам здесь для подсказок, это работает хорошо:

/* Disables all cursor overrides when body has this class. */
body.inheritCursors * {
    cursor: inherit !important;
}

Примечание. Мне не нужно использовать <html> и document.documentElement; вместо этого <body> и document.body работают нормально:

document.body.classList.add('inheritCursors');

Это вызывает все потомки элементов <body> (поскольку теперь у него есть этот класс inheritCursors), чтобы наследовать свой курсор от самого <body>, что бы вы ни установили:

document.body.style.cursor = 'progress';

Затем, чтобы вернуть управление элементам-потомкам, просто удалите класс:

document.body.classList.remove('inheritCursors');

и сбросить курсор на <body> на значение по умолчанию. :

document.body.style.cursor = 'unset';
person Andrew    schedule 22.07.2020
comment
⚠️ Примечание. Это (как и ожидалось) не работает в случае пользовательского элемента с теневым деревом, если теневое дерево содержит дочерний элемент с правилом курсора. - person Ian; 02.03.2021

Я попытался использовать setPointerCapture, и это сработало отлично. Недостатком является то, что (по причине) все события указателя не будут работать, как раньше. Так что я потерял стили наведения и т.д.

Мое решение теперь довольно простое и для моего варианта использования лучше подходит, чем приведенные выше решения CSS.

Чтобы установить курсор, я добавляю новую таблицу стилей в заголовок:

const cursorStyle = document.createElement('style');
cursorStyle.innerHTML = '*{cursor: grabbing!important;}';
cursorStyle.id = 'cursor-style';
document.head.appendChild(cursorStyle);

Чтобы сбросить его, я просто удаляю таблицу стилей:

document.getElementById('cursor-style').remove();
person Felix Engelmann    schedule 18.05.2021