The CSS3 resize property can be assigned to arbitrary elements. I'm looking for a way to detect such a resize on, say, divs (I don't mind it only working in Firefox at the moment):
div {
resize: horizontal;
overflow: hidden;
}
Unfortunately, the onresize event seems not to be fired on the div. How can I detect in JavaScript when such a user-instantiated resize has happened?
Edit: FWIW I had opened a bug report over at Mozilla. If you want to track it: https://bugzilla.mozilla.org/show_bug.cgi?id=701648
Resizing is like a style change. As such it can be observed with a MutationObserver. The more specific ResizeObserver is probably even better:
let observer = new ResizeObserver(function(mutations) {
console.log('mutations:', mutations);
});
let child = document.querySelector('textarea');
observer.observe(child, { attributes: true });
<textarea></textarea>
Listen to DOMAttrModified events. Got the idea from this answer, this jsFiddle appears to work in Firefox 8 (if you open the console).
Since the resize event clearly doesn't work (currently, at least), you can try one of these alternative options:
Use a combination of mousedown, mousemove and/or mouseup to tell whether the div is being / has been resized. If you want really fine-grained control you can check in every mousemove event how much / if the div has been resized. If you don't need that, you can simply not use mousemove at all and just measure the div in mousedown and mouseup and figure out if it was resized in the latter.
Poll every 200ms or so (depending on your needs) and compare the current size with the last known size. See setTimeout().
You can use the ResizeSensor class of the css-element-queries polyfill from
https://github.com/marcj/css-element-queries
It allows you to call a javascript function on size changes for all types of elements, not only for window. It sets up a real sensor, not a javascript setTimeout poll.
Use it like this:
new ResizeSensor($('#myelement'), function() {
console.log("myelement's size has changed");
});
Supported browsers are: all incl. IE6+.
This seemed to work pretty well for me:
$("body").on('mousedown mousemove', ".resizeItem", function () {
console.log($(this).height());
})
"Use a combination of mousedown, mousemove and/or mouseup"
as per Felixs answer.
Especially useful when combining a custom search result panel with Dev Extremes Scroll View and you want full control over it.
$("body").on('mousedown mousemove', ".scrollContainer", function () {
var h = $(this).height() - 20;
$(".scrollArea").dxScrollView('instance').option('height', h);
});
According to this site it only works in internet explorer 9.
Try this fiddle: http://jsfiddle.net/maniator/3Zva3/
Related
For a slider I use some neat jQuery magic to have an overlaying text scale with the height of the slider, so it never overlaps the slider itself. While this is working pretty good I am having trouble make jQuery recognize an orientation change on mobile devices as well as from changing from window to full screen and vice versa on desktop browsers. Is there a way to make the following listen to those changes as well?
fontresize = function () {
var fontSize = jQuery("#slider").height() * 0.34;
jQuery("#slider_caption").css('font-size', fontSize);
};
jQuery(window).resize(fontresize);
jQuery(window).load(fontresize);
Edit: I am using jQuery, not jQuery mobile. Would it be recommended, for only this small problem, to include jQuery mobile as well?
Theres the "orientationchange" event
$(window).on("orientationchange",function(){
alert("The orientation has changed!");
});
I believe that "on" is the best practice to handle events, in your case it would be:
$(window).on("orientationchange resize load", fontresize);
Edit: and here are som fullscreen change events:
$(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange', fontresize);
Edit: Since you are using a very outdated jQuery, maybe a JS event-binding works:
window.addEventListener('orientationchange', fontresize);
In my web application I try to implement some drag and drop functionality. I have a global JavaScript component which does the the basic stuff. This object is also responsible for changing the mouse cursor, depending of the current drag operation (move, copy, link). On my web page there are various HTML elements which define an own cursor style, either inline or via a CSS file.
So, is there a way for my central drag and drop component to change the mouse cursor globally, independent from the style of the element the mouse cursor is over?
I tried:
document.body.style.cursor = "move"
and
document.body.style.cursor = "move !important"
But it doesn't work. Every time I drag over an element which defines a cursor style, the cursor changes to that style.
Sure, I could change the style of the element I'm currently dragging over, but then I have to reset it when I leave the element. This seems a little bit to complicated. I'm looking for a global solution.
Important Update (2021):
The MDN page for element.setCapture() clearly indicates that this feature is deprecated and non-standard, and should not be used in production.
The browser support table at the bottom of that page indicates that it's only supported in Firefox and IE.
Original answer below
Please: don't massacre your CSS!
To implement a drag and drop functionality, you have to use a very important API: element.setCapture(), which does the following :
All mouse events are redirected to the target element of the capture, regardless of where they occured (even outside the browser window)
The cursor will be the cursor of the target element of the capture, regardless where the mouse pointer is.
You have to call element.releaseCapture() or document.releaseCapture() to switch back to normal mode at the end of the operation.
Beware of a naïve implementation of drag and drop: you can have a lot of painful issues, like for example (among others): what happens if the mouse is released outside the browser's window, or over an element which has a handler that stops propagation. Using setCapture() solves all this issues, and the cursor style as well.
You can read this excellent tutorial that explains this in detail if you want to implement the drag and drop yourself.
Maybe you could also use jQuery UI draggable if possible in your context.
I tried using setPointerCapture which worked great. The downside is, that (of cause) all pointer events will not work as before. So I lost hover styles etc.
My solution now is pretty straight forward and for my usecase better suited then the above CSS solutions.
To set the cursor, I add a new stylesheet to head:
const cursorStyle = document.createElement('style');
cursorStyle.innerHTML = '*{cursor: grabbing!important;}';
cursorStyle.id = 'cursor-style';
document.head.appendChild(cursorStyle);
To reset it, I simply remove the stylesheet:
document.getElementById('cursor-style').remove();
document.body.style.cursor = "move"
should work just fine.
However, I recommend to do the global styling via CSS.
define the following:
body{
cursor:move;
}
The problem is, that the defined cursors on the other elements override the body style.
You could do someting like this:
your-element.style.cursor = "inherit"; // (or "default")
to reset it to the inherited style from the body or with CSS:
body *{
cursor:inherit;
}
Note however, that * is normally considered a bad selector-choice.
Unfortunately element.setCapture() does not work for IE
I use a brute force approach - open a transparent div on top of entire page for the duration of drag-drop.
.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);
}
Thanks to some of the other answers here for clues, this works well:
/* Disables all cursor overrides when body has this class. */
body.inheritCursors * {
cursor: inherit !important;
}
Note: I didn't need to use <html> and document.documentElement; instead, <body> and document.body work just fine:
document.body.classList.add('inheritCursors');
This causes all descendant elements of <body> (since it now has this inheritCursors class) to inherit their cursor from <body> itself, which is whatever you set it to:
document.body.style.cursor = 'progress';
Then to yield back control to the descendant elements, simply remove the class:
document.body.classList.remove('inheritCursors');
and to unset the cursor on the <body> to the default do:
document.body.style.cursor = 'unset';
This is what I do and it works in Firefox, Chrome, Edge and IE as of 2017.
Add this CSS rule to your page:
html.reset-all-cursors *
{
cursor: inherit !important;
}
When the <html> element has the "reset-all-cursors" class, this overrides all cursors that are set for elements individually in their style attribute – without actually manipulating the elements themselves. No need to clean up all over the place.
Then when you want to override your cursor on the entire page with that of any element, e. g. the element being dragged, do this in JavaScript:
element.setCapture && element.setCapture();
$("html").addClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", $(element).css("cursor"), "important");
It uses the setCapture function where it is available. This is currently just Firefox although they say it's a Microsoft API. Then the special class is added to the entire document, which disables all custom cursors. Finally set the cursor you want on the document so it should appear everywhere.
In combination with capturing events, this may even extend the dragging cursor to outside of the page and the browser window. setCapture does this reliably in Firefox. But elsewhere it doesn't work every time, depending on the browser, its UI layout, and the path along which the mouse cursor leaves the window. ;-)
When you're finished, clean up:
element.releaseCapture && element.releaseCapture();
$("html").removeClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", "");
This includes jQuery for addClass and removeClass. In simple scenarios you could just plain compare and set the class attribute of document.documentElement. This will break other libraries like Modernizr though. You can get rid of the css function if you know the element's desired cursor already, or try something like element.style.cursor.
A performance-acceptable, but not ideal either, solution that I ended up using, is actually to change the cursor prop of element directly under the pointer, and then return it back to original when pointer moved to another element. It works comparatively fast, as just a few elements change their style while moving pointer around, but visually you might sometimes see a short glimpse of "original" cursor. I consider it a much more acceptable tradeoff.
So, the solution, in TypeScript:
let prevElement: HTMLElement | undefined;
let prevElementOriginalCursor: string | undefined;
export const setTemporaryCursor = (element: HTMLElement, cursor: string | undefined) => {
// First, process the incoming element ASAP
let elementOriginalCursor: string | undefined;
requestAnimationFrame(() => {
if (element && prevElement !== element) {
elementOriginalCursor = element.style.cursor;
element.style.cursor = cursor ?? '';
}
});
// Then process the previous element, not so critical
requestAnimationFrame(() => {
if (prevElement && prevElement !== element) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = elementOriginalCursor;
}
prevElement = element;
});
};
export const resetTemporaryCursor = () => {
if (prevElement) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = undefined;
prevElement = undefined;
}
};
just call setTemporaryCursor while user moves mouse, and call resetTemporaryCursor() when drag process is wrapped up (on MouseUp for instance).
This does the job for me. The use of requestAnimationFrame is optional, and probably could be improved with experimentation.
so suppose that clicking something would lead to a new content being loaded to the screen hence the height of document changes and whereas previously there are no scroll bars, now there actually are scrollbars...
how do I detect something like that happening using jquery
binding resize event onto window only detects window resize whereas binding it into document doesn't work
Update:
Please don't use the DOMSubtreeModified event. It is old, deprecated and not well-supported by browsers. In 99,9 % of the cases, there is a different event you can listen on. Most likely you are one of those people using jQuery and doing some AJAX stuff, so please take a look at their AJAX docs.
These are all available events. You would have to detect $(document).bind('DOMSubtreeModified', function() { ... }); and check for a dimension change to the previous firing.
var height = $(this).height(),
width = $(this).width();
$(document).bind('DOMSubtreeModified', function() {
if($(this).height() != height || $(this).width() != width) {
recalibrate();
}
});
This event is firing every time anything is done to the DOM. Therefore it will slowdown your browser.
We should get a better alternative. Could you please give us more information to your scenario?
Here is the solution.
// ajdust margins when page size changes (ie rotate mobile device)
$(window).resize(function() {
// Do something more useful
console.log('doc height is ' + $(window).height());
});
You could try a percentage scrolled event like this one:
http://www.jquery4u.com/snippets/jquery-detect-scrolled-page/
You might need to add a check to see whether the vertical scrollbar is present:
var hasVScroll = document.body.scrollHeight > document.body.clientHeight;
There are plenty of examples showing how to dynamically set an iframe's height to its content. This works perfect for me. The problem I'm now having is that the content can change size without triggering onload (think hidden/expandable divs).
Is there any way to detect when the size of the iframe content has changed? This is on the same domain and no jQuery, please.
I would do this by polling regularly (maybe every 200 milliseconds, perhaps more often) using setInterval. You could then compare the size of the content to what it was last time.
var iframe = document.getElementById('myIframe'),
lastheight;
setInterval(function(){
if (iframe.document.body.scrollheight != lastheight) {
// adjust the iframe's size
lastheight = iframe.document.body.scrollheight;
}
}, 200);
For non-webkit browsers, there are a few domMutation-Events, that fire when an attribute of an element (e.g. the body element) change. See DOMSubtreeModified and more importantly DOMAttrModified.
The internet explorer does fire the onresize event even on non-windows elements.
Opera honors the domMutation Events.
Webkit on the other discarded these events as a compromise to rendering speed and javascript-performance. Thee is no other way than to check via timeout/interval the effective size of an element.
ResizeObserver worked for me:
const iframe = document.getElementById('myIframe')
const observer = new ResizeObserver(() => {
setHeight(iframe.document.body.scrollHeight);
})
observer.observe(iframe.document.body);
I have a element on my website which is freely resizable. This is done by 4 handles on the edges. On hovering these handles and while resizing the element I want to show the respective resize arrows.
Currently I implemented this behavior by setting the css cursor style of the body/root to these arrows. The problem about it is the limit to the client area of the browser window. It would be visually more consistent and less confusing, if the arrow cursor would be visible everywhere while the mouse is hold down.
Google Maps is doing the same thing with their hand cursor while moving the map. So my question is how to achive this effect on my own.
My current (relevant) source:
function startObjectScaling(e){
e.stopPropagation();
e.preventDefault();
document.documentElement.style.cursor = this.style.cursor;
window.addEventListener("mouseup", stopObjectScaling, false);
}
function stopObjectScaling(e){
e.stopPropagation();
document.documentElement.style.cursor = '';
window.removeEventListener("mouseup", stopObjectScaling);
}
[...]
var tg = document.getElementById("transformGadget");
var handle = tg.firstChild.nextSibling;
for(var i=0;i<4;i++){
handle.addEventListener("mousedown", startObjectScaling, false);
handle = handle.nextSibling;
}
There is a special function implemented in the more modern browsers for this purpose. The name is setCapture(). It redirects all mouse input to the object the method was called on. Now a simple css cursor definition on that element is enough to archive the desired effect. After mouse release this effect stops (for security for sure). It can also be stopped manually by calling releaseCapture
example:
<style type="text/css">
#testObj {
/* this cursor will also stay outside the window.
could be set by the script at the mousedown event as well */
cursor: hand;
}
</style>
[...]
document.getElementById('testObj').onmousedown = function(e){
// these 2 might be useful in this context as well
//e.stopPropagation();
//e.preventDefault();
// here is the magic
e.target.setCapture();
}
if the arrow cursor would be visible everywhere while the mouse is hold down.
You're relying on a potential OS quirk to create your behavior. This is not something you can ASSUME will always hold true. However, once you start a mousedown, the cursor at that point will normally stay the same, no matter where you move the mouse to, UNTIL something else (another window that you may mouse over? the desktop? a system-interrupt?) changes the cursor.
In other words, don't rely on this behavior. Find something else that will work for you. If you must do this, re-examine your business requirements.