jQuery click on border of a div - javascript

I have a div that scrolls with a lot of text in it. My question is, is it possible to detect a click on the border of the div?
What I'd like to accomplish is if the user clicks on the bottom border (which is styled 4px wide with CSS), the div scrolls all the way to the bottom. Is this even possible without adding more markup?

You can try this:
$('div').click(function(e){
if(e.offsetY >$(this).outerHeight() - 4){
alert('clicked on the bottom border!');
}
});
Demo.
The .outerHeight() just returns the height of the content (including border). The e.offsetY returns the clicked Y relative to the element. Note about the outerHeight, if passing a bool true argument, it will include margin in the calculated value, the default is false, so it just returns content height + padding + border.
UPDATE: Looks like FireFox has its own way of behavior. You can see that when clicking, holding mouse down on an element, it's very natural and convenient to know the coordinates of the clicked point relative to the element. But looks like we have no convenient way to get that coordinates in the so-called FireFox because the e.offsetX and e.offsetY simply don't work (have no value). Instead you have to use the pageX and pageY to subtract the .offset().left and .offset().top respectively to get the coordinates relative to the element.
Updated demo

I never tried it, But I don't see why it shouldn't work :
Calculate the height of the element.
calculate the bottom border
calculate the offset inside the element itself, like in here
jQuery get mouse position within an element
Now you can check if the mouse position is inside the bottom border using some math.
I'm not sure how box-sizing fits into this, But that's how I would start around.

You have a wrapper around your element and set the padding to what ever you want to be detected.
jQuery
$("#border").click(function (e) {
if(e.target !== e.currentTarget) return;
console.log("border-clicked")
});
#border {
padding: 4px;
background: blue;
box-sizing: border-box;
cursor: pointer;
}
.box{
height: 100px;
width: 100%;
background: white;
cursor: default;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="border">
<div class="box"></div>
</div>
Vanilla JS
var border = document.getElementById("border");
border.onclick = function(e) {
if(e.target !== e.currentTarget) return;
console.log("border-clicked")
}
#border {
padding: 4px;
background: blue;
box-sizing: border-box;
cursor: pointer;
}
.box{
height: 100px;
width: 100%;
background: white;
cursor: default;
}
<div id="border">
<div class="box"></div>
</div>

Related

Why does the mouseout handler behave so illogically in this case?

Red square is the part of a container with class "parent". If I hover mouse over that red square it disappears. But why? I expected that it shouldn't.
Expected behaviour: it does not disappear since red square is a part of ".parent" container and I have clearly stated, that the mouseout event occurs on that container.
There was a suggestion, that this question is a duplicate of
JavaScript mouseover/mouseout issue with child element
In some way - yes, but I think that this question provides value, because it not only provides the solution ("you can try this"), but also explains WHY you should use that and WHY the initial solution is not working as it is supposed to.
<span class="parent">Hover mouse over this text<br></span>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append("<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>");
$(this).on("mouseout", removeSquare);
}
$(".parent").on("mouseover", addSquare);
</script>
It's normal behaviour of .mouseout() event.
Show the number of times mouseout and mouseleave events are triggered.
mouseout fires when the pointer moves out of the child element as
well, while mouseleave fires only when the pointer moves out of the
bound element.
You should use .mouseenter() and .mouseleave() events,
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append ( "<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>" );
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
$(".parent").on("mouseleave", removeSquare);
.parent {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="parent">Hover mouse over this text<br></span>
As other people have noted, your original problem is that mouseover and mouseout events also fire for child elements. The solution to that issue is either to use jQuery's mouseenter and mouseleave events, or simply to replace the JS code with the CSS :hover pseudo-class.
However, the reason why the other JS and CSS solutions posted here sometimes behave erratically (causing the square to disappear if you move the mouse over it slowly, but not if you move it fast, and not on all browsers even if you move it slowly) is because, depending on your browser and font settings, there may or may not be a small gap between the top line of text and the square below it. If the gap exists, and your mouse cursor hits it while moving from the text to the square, the browser will consider the mouse to have left the parent element, and will thus hide the square.
Setting a (light blue) background color on the parent element shows the issue clearly; depending on what font and line height the browser chooses, the parent element and the box can look like this:
or like this:
Manually setting a particularly large line height makes the problem easily reproducible (CSS example based on Thomas van Broekhoven's answer):
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
background-color: red;
width: 50px; height: 50px;
}
.parent {
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>
There are two general ways to fix this issue. The simplest option, where practical, is to make the parent element a block, thereby eliminating the gaps between the lines. You may also wish to add position: absolute to the square's style, so that it won't expand its parent element when it appears:
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
position: absolute;
background-color: red;
width: 50px; height: 50px;
}
.parent {
display: block;
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>
Alternatively, if you really want to stick with an inline parent element (e.g. because you want it to be able to wrap across several lines of text), you can set a negative top margin on the square to make sure it overlaps the line of text above it. If you don't want the square to visibly overlap the text, you can further move all the visible content of the square into an inner element and set a corresponding positive top margin on it, like this:
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
position: absolute;
margin-top: -1em;
border: 1px dashed gray; /* to show the extent of this otherwise invisible element */
}
.kvadrat > .inner {
display: block;
margin-top: 1em;
background-color: red;
width: 50px; height: 50px;
}
.parent {
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'><span class='inner'></span></span></span>
I know this is not directly answering your JavaScript question, but I would like to open your eyes if you're not bounded to JavaScript. You can easily achieve this with CSS.
.kvadrat {
display: none:
}
.parent:hover > .kvadrat {
display: inline-block;
background-color: red;
width: 50px;height: 50px;
}
<span class="parent">Hover mouse over this text<br>
<span class='kvadrat'></span></span>
You can achieve the same using CSS.
.child {
display: none:
}
.parent:hover > .child {
display: inline-block;
background-color: red;
width: 50px;
height: 50px;
}
<span class="parent">Hover mouse over this text<br>
<span class='child'></span>
</span>
It is because of event bubbling. When you enter the child span, you jQuery will fire mouseout because you've now gone to a child span. If you want to keep it going, use mouseenter and louseleave which does not fire until you leave the actual element, regardless of child elements.
<span class="parent">Hover mouse over this text<br></span>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append ( "<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>" );
$(this).on("mouseleave", removeSquare);
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
</script>

Create a segmented control-like with a draggable function

I have 2 divs positioned next to each other, and a background div. We'll call the background div "bg div". Whenever one of the 2 divs get selected, the bg div gets positioned on top of the selected div with a transition. Basically, something like a segmented controller.
The next step I want to do is, I want to make the bg div draggable. If it gets dragged, but not all the way to either side, then it should snap to whichever side the bg div is mostly at.
I'm looking for something like this:
When I set the bg div to be draggable, (using JQuery UI) it wasn't draggable. Only when I removed z-index: -1 did it become draggable. It also didn't snap to either side. It only snapped when the bg div got dragged basically all the way. Also, when I drag it, it has a weird effect to it. It waits a bit then drags. I think that's because the transition.
Problems
How can I make it draggable with of index: -1?
How can I make it snap to whichever side the bg div is mostly at?
How can I make it have a transition without working weird?
Without issues, but not draggable functionality: JSFiddle
With issues: JSFiddle
$('#bckgrnd').draggable({
axis: "x",
containment: "parent",
snap: ".labels"
});
#radios {
position: relative;
width: 370px;
}
input {
display: none;
}
#bckgrnd,
#bckgrndClear,
.labels {
width: 50%;
height: 30px;
text-align: center;
display: inline-block;
float: left;
padding-top: 10px;
cursor: pointer;
}
.labels {
outline: 1px solid green;
}
#bckgrnd {
background-color: orange;
position: absolute;
left: 0;
top: 0;
transition: left linear 0.3s;
}
#rad1:checked ~ #bckgrnd {
left: 0;
}
#rad2:checked ~ #bckgrnd {
left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="radios">
<input id="rad1" type="radio" name="radioBtn" checked>
<label class="labels" for="rad1">First Option</label>
<input id="rad2" type="radio" name="radioBtn">
<label class="labels" for="rad2">Second Option</label>
<div id="bckgrnd"></div>
</div>
Working solution: http://jsfiddle.net/6j0538cr/ (I know you wont use it :))
How can I make it draggable with of index: -1?
Add two elements one that hold the label with 'pointer-events:none;' which will ignore all mouse events and 'z-index:3',
And second that will be the 'button' and will have 'z-index:1'.
Like that you will have one label that ignores all mouse events and float above all the elements with z-index:3, and the 'background' will still be draggable
How can I make it snap to whichever side the bg div is mostly at?
You can calculate it very easily using 'offset' and 'width' functions like so
//calculating the middle of the 'background'
var backgroundX = $('#bckgrnd').offset().left;
var backgroundWidth = $('#bckgrnd').outerWidth();
var backgroundMiddle = backgroundX + (backgroundWidth/2);
//calculating the middle of the radios on the page
var radiosX = $('#radios').offset().left;
var radiosWidth = $('#radios').outerWidth();
var radiosMiddle = radiosX + (radiosWidth/2);
//compare the two
if(radiosMiddle > backgroundMiddle){
//closer to the left
}else{
//closer to the right
}
How can I make it have a transition without working weird?
You can set the transition using jQuery 'animate' instad of mixing css and js animation.
draggable with index -1: Have a container div for the whole thing (radios?) trapping mouse events. mousedown you record the mouse x value. mousemove (with button down I suppose) you calculate the "delta" from current mouse x to mousedown x, and add that to the original x value of the thing you are "dragging".
snapping: just using min / max function to limit the delta, but it sounds like some animation is there even for the "snap". So for a mouse up within a certain end zone range, fire that animation to "snap" to one side or the other.
Also, the click inside certain bounds close to the edge fires that same snap.
I've could probably done this using <input> radio elements...
But any way on submit you can send the data-* value.
Here's my take:
$(".io-toggler").each(function(){
var io = $(this).data("io"),
$opts = $(this).find(".io-options"),
$clon = $opts.clone(),
$span = $clon.find("span"),
width = $opts.width()/2;
$(this).append($clon);
function swap(x) {
$clon.stop().animate({left: x}, 150);
$span.stop().animate({left: -x}, 150);
$(this).data("io", x===0 ? 0 : 1);
}
$clon.draggable({
axis:"x",
containment:"parent",
drag:function(evt, ui){
$span.css({left: -ui.position.left});
},
stop:function(evt, ui){
swap( ui.position.left < width/2 ? 0 : width );
}
});
$opts.on("click", function(){
swap( $clon.position().left>0 ? 0 : width );
});
// Read and set initial predefined data-io
if(!!io)$opts.trigger("click");
// on submit read $(".io-toggler").data("io") value
});
.io-toggler{
cursor:pointer;
display:inline-block;
position:relative;
font:20px/1.5 sans-serif;
background:url(http://i.stack.imgur.com/XVCAQ.png);
color:#fff;
border:4px solid transparent;
border-radius: 50px;
user-select:none;
-webkit-user-select:none;
-webkit-touch-callout:none;
}
.io-options{
border-radius:50px;
top:0;
left:0;
overflow:hidden;
}
.io-options span{
position:relative;
text-align:center;
display:inline-block;
padding: 3px 35px;
}
/* the jQ clone */
.io-options + .io-options{
position:absolute;
background:#fff;
width:50%;
height:100%;
white-space:nowrap;
}
.io-options + .io-options span{
color:#006cff;
}
<link href="https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css" rel="stylesheet" type="text/css" />
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<span class="io-toggler" data-io="0">
<span class="io-options">
<span>Thing1</span>
<span>Thing2</span>
</span>
</span>
<br><br>
<span class="io-toggler" data-io="1">
<span class="io-options">
<span>Yes</span>
<span>No</span>
</span>
</span>

Is scrollTop, and scrollLeft for overflow hidden elements reliable?

I accidentally discovered that scrollTop, and scrollLeft on an element work even when an element is overflow: hidden. Can this behaviour be relied on?
Supposedly scrollTop, and scrollLeft are supposed to be zero for elements without scrollbars, and setting them on such elements is supposed to have no effect.
Yes, even if an element has CSS overflow set to hidden,
Javascript Element.scrollTop(), Element.scrollLeft() allows you to manipulate the element's scroll position if the element contains overflowing children.
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop
Here's a quick use case:
Animate gallery using scrollLeft
var GAL = $("#gal"),
n = GAL.find(">*").length;
c = 0;
$("button").on("click", function(){
GAL.animate({ scrollLeft: (++c%n) * GAL.width() });
});
#gal {
height: 40vh;
overflow: hidden; /* !! */
white-space:nowrap;
font-size: 0;
} #gal>* {
font-size: 1rem;
display: inline-block;
vertical-align: top;
width: 100%;
height: inherit;
background: 50% / cover;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="gal">
<div style="background-image:url(//placehold.it/800x600/cf5);"></div>
<div style="background-image:url(//placehold.it/800x600/f0f);"></div>
<div style="background-image:url(//placehold.it/800x600/444);"></div>
</div>
<button>scrollLeft</button>
Not sure yet why Chrome does not do the following but:
Firefox will remember your gallery scroll-position on historyBack (navigating back to the page where you scrolled your gallery)

Get dimensions of text block via JavaScript, not the size of container's `getBoundingClientRect`

I want to get the size of text inside a container. Let's consider general case when the container has padding and border.
The problem is that getBoundingClientRect returns the size of text PLUS left border and padding, in case the text overflows. Otherwise it returns just the size of border box of the container.
You can get the width if you create a placeholder div with all of the same text formatting options and find it's width.
For instance, I will create a div with the class .hidden that has the same attributes as the original div.
div.container
{
font-size: 16px;
}
div.hidden
{
font-size: 16px;
display: none;
}
Then, using jQuery, copy the contents of .container to .hidden and find the width of .hidden:
$(function(){
$("div.container").each(function(){
$("body").append("<div class='hidden'>"+$(this).html()+"</div>");
var width = $("div.hidden").width();
$("div.width").html("Actual width: "+width+"px");
$("div.hidden").remove();
});
});
JSFiddle
Interesting! You could use javascript to clone the text inside of an empty element offscreen that has 0 padding/margin/border. Then you could get the width of that element.
var txt = document.getElementById('fixed').innerHTML,
clone = document.getElementById('clone');
clone.innerHTML = txt;
var width = clone.offsetWidth;
document.getElementById('output').innerHTML = width;
#fixed {
width: 8em;
height: 8em;
border: .5em solid red;
}
#clone {
margin: 0;
padding: 0;
border: 0;
position: fixed;
left: -9999px;
}
<div id="fixed">asdfkjahsdflkahjsdflkjhasdljfhalsdkjfhalsdkjfhalsdkjfhalksdhjflasd</div>
<div id="clone"></div>
Width of text: <span id="output"></span>
People who had answered here came with a brilliant idea of wrapping the text into a <div> having zero margin, border and padding;
I just developed the idea further. I place the div inside the container, making the text have exactly the same style as it had without wrapper.
JsFiddle
This solution will work almost everywhere. It can be broken by not very encouraged way of writing CSS, like
.container div b {
padding: 5px; /* firing only when test is run */
}
If you do not code CSS in you project like that, you are the lucky one to use my snippet )

How to force wrapper div to keep its optimal width according to its children

I have a problem here in Chrome (maybe in other browsers too).
I have a wrapper div which floats to the left. It contains some child div and these divs float to the left too and they have different widths according to their contents. I defined a max-width property for the wrapper div and as you can see in the fiddle code when the wrapper reaches this max-width, the last child in the wrapper moves to the next line but the wrapper keeps the maximum width and there are a lot of empty space on the right.
I'd assume the wrapper's size should be recalculated and should have a smaller width because it floats.
I'd like it, because in my real code the wrapper has "sexy" css but it looks rude with empty spaces.
Sorry for my English, I hope you'll understand my problem. Has anyone an idea how I can resolve this problem without any JS (or just a little bit of JS)?
Thanks!
http://jsfiddle.net/Xd9PV/1/
<div class="wrapper">
<div class="float float-1">apple</div>
<div class="float float-2">banana</div>
<div class="float float-3">orange</div>
<div class="float float-4">some very delicious strawberries</div>
</div>​
.wrapper {
border: 1px solid red;
float: left;
max-width: 300px;
}
.float {
border: 1px solid blue;
float: left;
}
It’s not possible using CSS. When you set a max-width, it won’t recalculate it’s width after x floats have dropped to other lines because of the overflow.
You can use javascript/jQuery to calculate this for you instead that inserts a break where needed, f.ex:
var width = 0,
maxWidth = 300, w;
$('.wrapper .float').each(function() {
width += ( w = $(this).outerWidth() );
if ( width > maxWidth ) {
$(this).before('<div style="clear:left"></div>');
width = w;
}
});​
Demo: http://jsfiddle.net/2mfbY/
Add a clear:both to the last div element like this:
.float {
border: 1px solid blue;
float: left;
margin-right: 5px;
}
.float:last-child {
clear:both;
}
Works for me.
You can try using ellipsis (But I am not sure whether it seems OK to you or not)
.float {
border: 1px solid blue;
float: left; padding:0 2px;
margin-right: 5px; max-width:90px;
text-overflow:ellipsis ;
white-space:nowrap; overflow:hidden
}
​
DEMO

Categories

Resources