I'm implementing a "resize handle" to change the width of my left navigation panel. It is a div that receives an onMouseDown() event, calculates the necessary widths and applies them to the right elements in the subsequent calls to onMouseMove(). But I'm having some problems.
1) The article element, to the right of the navigation panel and handle, does not activate the onMouseUp() if I release the mouse there. Is this because the onMouseDown() was activated in other element?
2) If I move the mouse fast to the right, I can't prevent the text in the article from being selected, even calling methods like preventDefault() and stopPropagation().
3) Even if there's no text in the article, the resizing only works if I move the mouse very slowly. If the mouse moves fast over the article element, the resize stops, unless I release the mouse button - in this case the resize goes smoothly (suggesting it was the text-selecting that was stopping the resize, even with no text at all). But if I release the mouse button, the resize should stop (see point 1).
I've seen some solutions using CSS user-select: none, but this would prevent any text from being selected inside article, which is obviously an overkill.
So, how can I make the resizing smooth, without selecting any text, when I move the mouse over any element in my document? (After pressing the button in the right div, of course.)
That's my HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>CSS Template</title>
</head>
<body>
<header>Header</header>
<main>
<nav id='nav'>
<div class='navcont' onmousemove='handMm(event)' onmouseup='handMu(event)'>
<p>nav 1</p>
<p>nav 2</p>
</div>
<div class='handle' onmousedown='handMd(event)' onmousemove='handMm(event)' onmouseup='handMu(event)'>
</div>
</nav>
<article id='article' onmousemove='handMm(event)' onmouseup='handMu(event)'>
</article>
</main>
<footer>Footer</footer>
</body>
</html>
That's my CSS:
html, body {
height:100%;
margin:0;
}
body {
display:flex;
flex-direction:column;
}
header {
text-align:center;
}
main {
flex:1;
display:flex;
min-height:0;
}
article {
background:#CCC;
width:auto;
overflow:auto;
padding:10px;
flex-grow:1;
}
nav {
width:300px;
height:auto;
overflow: hidden;
display:flex;
}
.navcont {
background:#8C8;
width:auto;
flex-grow:1;
}
.handle {
background:#333;
right:0px;
width:30px;
cursor:col-resize;
}
footer {
text-align:center;
}
That's my Javascript:
var mx,px,moving=false;
function handMd(e) {
mx = e.pageX;
px = document.getElementById('nav').clientWidth;
moving = true;
}
function handMm(e) {
if (moving) {
e.preventDefault();
e.stopPropagation();
e.cancelBubble = true;
e.returnValue = false;
var diff = e.pageX - mx;
document.getElementById('nav').style.width = (px + diff)+'px';
document.getElementById('article').style.width = (window.innerWidth-px-diff)+'px';
}
}
function handMu(e) {
moving = false;
}
And here is a working example: https://jsfiddle.net/45h7vq7u/
Another example, including Ryan Tsui's answer: https://jsfiddle.net/v1cmk2f6/1/ (the text-selection is gone, but the div still won't move smoothly, but only when moving fast to the right).
Catch the events of start moving and finish moving with your preferred method (onMouseDown and onMouseUp are fine). Add a CSS class to specify the moving state when the action starts. Remove the CSS class when the action finishes.
In your case, you may try the followings:
Add a new CSS class:
.moving {
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
Extend your handMd() and handMu() functions:
function handMd(e) {
mx = e.pageX;
px = document.getElementById('nav').clientWidth;
moving = true;
document.getElementById('article').classList.toggle('moving', true); // Add this line. 'article' is the id of the element where you don't want the selection to occur.
}
function handMu(e) {
moving = false;
document.getElementById('article').classList.toggle('moving', false); // Add this line. 'article' is the id of the element where you don't want the selection to occur.
}
After analyzing how this was solved somewhere else, I came to the following changes.
HTML - only one event handler needed:
...
<main>
<nav id='nav'>
<div class='navcont'>
<p>nav 1</p>
<p>nav 2</p>
</div>
<div class='handle' onmousedown='handMd(event)'>
</div>
</nav>
<article id='article'>
</article>
</main>
...
Javascript - attach and detach the onmousemove event handler is way better than calling onmousemove every time the mouse moves (to only then test if the mouse button has been pressed). Also, the event is now attached to the document, not to each div on screen:
var mx,px,minW = 200;
function handMd(e) {
mx = e.pageX;
px = document.getElementById('nav').clientWidth;
document.addEventListener('mousemove',handMm);
document.addEventListener('mouseup',handMu);
document.getElementById('article').classList.toggle('moving',true);
document.getElementById('nav').classList.toggle('moving',true);
}
function handMm(e) {
var diff = e.pageX - mx;
if (px+diff >= minW && window.innerWidth-px-diff >= minW) {
document.getElementById('nav').style.width = (px+diff)+'px';
document.getElementById('article').style.width = (window.innerWidth-px-diff)+'px';
}
}
function handMu(e) {
document.removeEventListener('mousemove',handMm);
document.removeEventListener('mouseup',handMu);
document.getElementById('article').classList.toggle('moving',false);
document.getElementById('nav').classList.toggle('moving',false);
}
CSS - thanks to Ryan Tsui's answer, I eliminated the unwanted text selection while resizing:
.moving {
user-select:none;
-moz-user-select:none;
-webkit-user-select:none;
-ms-user-select:none;
}
I don't know how to stop the selection but I can tell you for some help that the event triggered when something is selected is onselect.
Related
I want to display div1 on hovering div2 and disappear div1 only if mouse is not hovering both div1 and div2.
I tried using the following CSS and jquery. But the div1 disappears immadiately after unhovering div2 and i am unable to access the content of div1.
$(document).ready(function() {
$('.about').hover(
function() {
$('.showsection').slideDown(800).addClass('show');
}
, function() {
$('.showsection').slideToggle(800);
});
});
.showsection{
display:none;
}
.show{
display:block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class=about>
<h1>About</h1>
</div>
<div class="showsection">
<h1>Title</h1>
</div>
This could be done by attaching mouseenter and mouseleave events to the elements you want to show/hide.
These are the requirements:
Show showsection when mouse enters about. This can be done using mouseenter on about
Hide showsection when mouse is not hovering over both showsection and about. This actually means checking two things: the mouse is not hovering showsection when it leaves about and the mouse is not hovering about when it leaves showsection. That means we have to attach mouseleave events to both showsection and about.
The below snippet should help.
// JS
$(document).ready(function() {
// mouse enters .about
$('.about').on('mouseenter', function() {
$('.showsection').slideDown(800);
});
// mouse leaves .about
$('.about').on('mouseleave', function() {
// if mouse is not hovering over .showsection hide it
if (!$('.showsection').is(':hover')) {
$('.showsection').slideToggle(800);
}
});
// mouse leaves .showsection
$('.showsection').on('mouseleave', function() {
// if mouse is not hovering over .about hide .showsection
if (!$('.about').is(':hover')) {
$('.showsection').slideToggle(800);
}
});
});
/* CSS */
.showsection {
display: none;
background: #ddd;
}
h1 { margin: 0; }
.about { background: #eee; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class=about>
<h1>About</h1>
</div>
<div class="showsection">
<h1>Title</h1>
</div>
The CSS :hover attribute will only control the element which it is assigned to, in your case presumable div1. So, you are going to have to use JavaScript.
With JavaScript attach a mouseenter and mouseleave event to div1. Inside those event listener functions, control what you want div2 to do.
That's basically how to do it.
I have a div containing three buttons. The div needs to be draggable, so that you can drag all three buttons around the screen together. That works fine, but the problem is that when I click on of the individual buttons it inherits the draggable id and it is draggable on it's own. I do not want that to happen. So my question is: how do I make my buttons draggable, but make them always stay together and keep them clickable. I added the code below, but here is a JSFiddle: http://jsfiddle.net/2ga50vvt/
So to be clear: the div also needs to be draggable through dragging one of the individual buttons, but then the rest of the div needs to stick with it. Now dragging an individual button only moves the button.
P.S. I do not want to use JQuery UI
HTML:
<div id="draggable" class="ui-widget-content">
<button ng-click="menu.shown = !menu.shown">MENU</button>
<br>
<button ng-click="disconnect()">CLOSE</button>
<br>
<button ng-click="">KEYS</button>
</div>
JS
$(document).ready(function() {
var $dragging = null;
$('body').on("mousedown", "#draggable", function(e) {
$(this).attr('unselectable', 'on').addClass('dragged');
var el_w = $('.dragged').outerWidth(),
el_h = $('.dragged').outerHeight();
$('body').on("mousemove", function(e) {
if ($dragging) {
$dragging.offset({
top: e.pageY - el_h / 2,
left: e.pageX - el_w / 2
});
}
});
$dragging = $(e.target);
}).on("mouseup", ".dragged", function(e) {
$dragging = null;
$(this).removeAttr('unselectable').removeClass('dragged');
});
});
CSS:
body {
padding: 50px;
}
.dragged {
background-color: yellow;
}
#draggable {
position: fixed;
width: 150px;
height 150px;
padding: 0.5em;
background: red;
background-color: black;
z-index: 1000;
cursor: move;
float: left;
}
Update 1
This is a working solution: http://jsfiddle.net/2ga50vvt/3/
However when I click on the div and start dragging the center of the div jumps to my cursor. It works great, but it looks a bit wonky. Is there a way to prevent the div from moving to my cursor?
Your help is most welcome.
You can read the target property of the event and return false to avoid all not #draggable to be draggable.
if(e.target.id !== "draggable") {
return false;
}
The edited fiddle:
http://jsfiddle.net/2ga50vvt/1/
It works perfectly, but one suggestion: don't target with ids because with this code you can't drag more of one element (ids must be unique), so the workaround is to write an attribute or a classname and play with it.
Good luck.
Use $dragging = $('#draggable'); instead of $dragging = $('e.target');
It will drag div if you try to drag using cursor on button. It will drag #draggable instead of target.
Working Fiddle
Presuming you're opposed to JQueryUI for it's file size, I'd still recommend a prebuilt solution because why reinvent the wheel?
Draggabilly is a really nifty library that I've used when resource size has been an issue. It's 20k minified (obviously even smaller gzipped) and available on a CDN - which in itself has lots of benefits e.g. caching.
$(function() {
$( "#draggable" ).draggabilly();
});
There's a few CSS hooks, different options, events etc.
JSFiddle here
I haven't been able to find the answer to this anywhere.
How do you make a hidden div appear when mousing over where it would have been?
Please do not tell me how to make a link, I know how to make a link ;)
I have tried:
a.) onmouseover set visibility to visible and onmouseout set visibility to hidden
this works in 0 browsers
b.) setting borders to 0px and background transparent and innerhtml to "" onmouseout and reverting onmouseover
this works in chrome
c.) This was the most popular answer on the internet, which i knew wouldn't work, but I tried anyway: make a container div set to visible and then do visibility visible and visibility hidden for the inner div
d.) Setting opacity to 1/100 and 0
works in chrome
e.) last resort: i tried making a transparent gif and having it display onmouseout
this also failed
I haven't tried jquery's .hover but I have read that it may not work correctly.
I have no other ideas. Will somebody help, please?
If I get it right you want div element to show if you are over it and hide when the mouse is not over. If that's it you can do it only with html and css:
<head>
<style>
#outerDiv{width:100px;height:100px;background-color:blue;}
#innerDiv{width:100px;height:100px;background-color:red;display:none;}
#outerDiv:hover #innerDiv {display:block;}
</style>
</head>
<html>
<div id="outerDiv">
<div id="innerDiv">some text</div>
</div>
</html>
The outer div is always visible and when it's hovered the inner one is shown.
I think that this is going to help you: http://jsfiddle.net/eb4x9/
The mouseover event won't trigger when the div is hidden so you can detect it's position and size.
Here is the source:
HTML
<div id="foo"></div>
CSS
#foo {
width: 300px;
height: 300px;
background-color: red;
visibility: hidden;
}
JS
$(document).mousemove(function (event) {
var div = $('#foo'),
divLeft = div.offset().left,
divTop = div.offset().top,
divWidth = div.width(),
divHeight = div.height();
if ((event.pageX >= divLeft && event.pageX <= divLeft + divWidth) &&
(event.pageY >= divTop && event.pageY <= divTop + divHeight)) {
div.css('visibility', 'visible');
} else {
div.css('visibility', 'hidden');
}
});
$(document).mouseleave(function (event) {
var div = $('#foo');
div.css('visibility', 'hidden');
});
Best regards!
Try setting the display attribute of the div to 'block' along with the visibility attribute to 'visible' in your onmouseover event.
Set the display to 'none' and visibility to 'hidden' to hide.
Of course the trouble will be firing the mouse over on a hidden div.
This works in every browser I have ever used it in.
Try this, having two divs one empty and other with your content and toggling between them on mouseover
<html>
<head>
<script>
function toggle() {
var your_div = document.getElementById("your_div");
var empty_div = document.getElementById("empty_div");
if(your_div.style.display == "block") {
your_div.style.display = "none";
empty_div.style.display = "block";
}
else {
your_div.style.display = "block";
empty_div.style.display = "none";
}
}
</script>
<style>
#empty_div{width:100px; height:100px;}
#your_div{width:100px; height:100px; border: 1px solid #000fff;}
</style>
</head>
<body>
<div id="your_div" onmouseover="toggle()">xyz</div>
<div id="empty_div" onmouseover="toggle()"></div>
</body>
</html>
I've got a number of divs on a page that I can drag and drop around. I also implemented the blind effect on them so that I can minimize and maximize the content if i don't want to see it.
I've got a problem that if I have 3 items stacked on top of each other, vertically, and I move the bottom one to the right of the top one and minimize the top div, everything slides upwards - and the 3rd div that I moved up slides right off the screen!
I've tried a ton of stuff, like making divs use absolute positioning but that causes problems of divs not sliding upwards in some circumstances. Reordering the divs dynamically causes the divs to be thrown around the screen because of offsets and relative positioning.
I just want it so that when the user drags divs over to the left or right and an "earlier" div is minimized, all subsequent divs don't get moved.
Any suggestions on this one are greatly appreciated.
Edit 1:
The problem I'm having with the absolute positioning is as follows.
I start with A, B, and C in a vertical column. All items are expanded. I move B to the right side of A and C right under B. This gives me 2 columns (A being one and B,C being the other). With everything being absolute, if I try to close B, then C doesn't move up - rightfully so.
I tried then making things "selectively" absolute, thereby flipping between relative and absolute but I got into a problem with coordinates. If you have a relative position and left:100px and top:-50px, then flipping the position to absolute causes these coordinates to be interpreted within an absolute context. My control flies off the screen. I tried fixing this by getting the absolute coordinates using jQuery's offset function, however this returns the relative coordinates and I'm stuck. I tried to maintain the absolute coordinates myself, but it didn't work either for some reason. It's getting out of control :).
Javascript
This javascript bind is called when the page is loaded. I bind this function to a PNG arrow so that when the arrow is pressed, the content in the appropriate div expands and contracts.
$('.ArrowMargin').bind('click', function () {
var splits = this.src.split("/");
var action = "";
if (splits.length >= 2) {
var folder = splits[splits.length - 2];
var image = splits[splits.length - 1];
if (folder == "Images") {
if (image == "arrow_open.png") {
action = "close";
this.src = "Images/arrow_closed.png";
} else {
action = "open";
this.src = "Images/arrow_open.png";
}
}
}
var divs = document.getElementsByTagName("div");
if (action != "") {
var options = {};
for (var i = 0; i < divs.length; i++) {
var element = divs[i];
if (element.className.indexOf("Hideable") != -1) {
if (this.parentNode.parentNode == element.parentNode) {
if (action == "open") {
var jQueryObj = jQuery(element);
jQueryObj.show("blind", options, 500, null);
} else {
var jQueryObj = jQuery(element);
jQueryObj.hide("blind", options, 500, null);
}
break;
}
}
}
CSS
This is the CSS stuff where I set up some simple styles. I have some empty styles that I use to access divs based on class.
.ArrowMargin { float:right; margin:0 5px 0 0; }
.alpha { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.bravo { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.delta { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px;}
.charlie{}
.echo{}
HTML BODY
This is the body. It's just a number of divs that represent different pieces of content. The divs marked with the "Hideable" class are those that are jQuery blinded.
<body>
<div class="alpha">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar1</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable Center"></div>
</div>
<div class="bravo">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar2</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable charlie"></div>
</div>
<div class="delta">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar3</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable echo"></div>
</div>
</body>
I want to make a resizable block (<div>). It must resize when user grab the top left corner of the block. It is something like http://jqueryui.com/demos/resizable/ but for top left corner, not for bottom right.
HTML code:
<div class="chatBlockBody">
<img src="resizeMarker.png" class="topMarker" alt="" />
content of block
</div>
CSS code:
.chatBlockBody
{
width:240px;
height:250px;
border:1px solid #4a73ce;
position:absolute;
bottom:25px;
right:0;
display:block;
background-color:White;
}
.topMarker
{
position:absolute;
top:3px;
left:3px;
}
The js-code is following:
var dragObject;
$(".topMarker").mousedown(function(e){
dragObject = this;
$(dragObject).parent().css("z-index",42);
return false;
});
$(document).mouseup(function() {
dragObject = null;
});
$(document).mousemove(function(event){
if(dragObject!=null){
var hg=Math.max($(document).height()-event.pageY,250);
var wd=Math.max($(document).width()-event.pageX-parseInt($(dragObject).parent().css("right")), 240);
$(dragObject).parent().css("height",hg+"px");
$(dragObject).parent().css("width",wd+"px");
}
});
It works fine for Chrome and Firefox. But it didn't work in IE.
IE doesn't execute the mousemove code, when user move mouse with pressed mouse button. So, it didn't redraw the border of div.
How can I fix it and make resizable block for IE?
If jqueryui.resizable is satisfying except the position of the handle, you can use the handles-option there, the handles can be defined on every side you want to:
$('.chatBlockBody').resizable({ handles: 'nw' });