JS HTML5 Drag and Drop: Custom Dock Effect Jumping Around in Chrome - javascript

Situation: I'm using HTML5 drag-and-drop to place tiles in a game I'm writing. I'd like to add an effect where the two tiles that I'm about to drop a new tile between move slightly apart to indicate that this is where you're dropping (similar to the Mac OS dock).
My Approach: I have a flexbox into which I'm dropping these tiles. I wrote a function that essentially returns one period of a sine wave and I'm using it to update the dropped tiles' right: and top: CSS properties (the tiles are position: relative;) based on their original position relative to the mouse during drag.
// Update occupant style for desired effect
occupants.forEach(function(occupant, index) {
$(occupant).css({'right' : -10 * nudgeSine(occupantsMouseOffset[index] * 10) + 'px',
'top' : -10 * Math.abs(nudgeSine(occupantsMouseOffset[index] * 10)) + 'px',
'opacity' : 1 - Math.abs(nudgeSine(occupantsMouseOffset[index])) });
});
// Function to return 1 period of a sine wave
function nudgeSine(x) {
if (x < -3.14159 || x > 3.14159) {
return 0;
} else {
return Math.sin(x);
}
}
Problem: In Chrome (but not in Firefox), at some mouse positions, which I can't find a pattern in, the tile is jumping back-and-forth. See the .gif below:
In Chrome (left) and in Firefox (right):
I even console.logged the element's calculated right: property, and while it is shown jumping around on screen, it outputs as a constant value.
What I've Tried/Thought About:
Even with the mouse stationary and console.log(event.clientX) outputting a constant value, the tile will jump around.
I thought event.clientX might be changing imperceptibly, so I'm basing my calculations on Math.trunc(event.clientX) to no avail.
I am using element.getBoundingClientRect() in my calculations, which I'm not very familiar with, and I think it may be the root cause of my problem.
I made this CodePen, but wasn't able to completely replicate the issue. Still, I think someone may be able to spot what's happening.
Edit: I've put this up on a github page to fully replicate. This link may not work for future readers of the question, but I'll keep it up for the foreseeable future. To demonstrate the issue, view in Chrome and Firefox.
Thank you.

Perhaps I can expand my answer later, but for now:
Related questions: How to keep child elements from interfering with HTML5 dragover and drop events?
'dragleave' of parent element fires when dragging over children elements
This is what happens:
- you start dragging the operator
- operator moves over the box, existing operators move along nicely
- you move the operator over one of the existing operators
- at this point the browser enters a kind of infinite loop thingy, because each time the elements move the position of the elements have to be updated again (because new events are triggered)
Since you need the click event on the existing operators you can't just set them to pointer-events: none; like in the related question, but you can add a class when you start dragging and apply this style to the operators while you're dragging.
Another solution would be to use a library, in the comments of an answer I found the library https://bensmithett.github.io/dragster/, I use draggable by shopify.
update
I wasn't able to find the exact term of this behavior, perhaps we could go with "cyclic case" or "undefined behaviour". See my examples:
:root {
/*colors by clrs.cc*/
--navy: #001f3f;
--blue: #0074D9;
--red: #FF4136;
font-family: sans-serif;
}
.animated {
transition: all .5s;
}
h2 {
color: var(--red);
}
div {
height: 160px;
width: 160px;
padding: 20px;
background: var(--blue);
margin-bottom: 20px;
}
.box1 {
border-right: 20px solid var(--navy);
}
.box1:hover {
border-right: 0px solid var(--navy);
}
.box2:hover {
border-radius: 100px;
}
<div class="box1 animated">hover your mouse over my border on the right →</div>
<div class="box2 animated">hover your mouse over an edge of this box</div>
<h2>Warning, the following boxes have no animations, flashes are expected:</h2>
<div class="box1">hover your mouse over my border on the right →</div>
<div class="box2">hover your mouse over an edge of this box</div>
When the user moves the mouse onto the border the following happens in a loop:
box1 is being hovered
hover styles apply, the border is removed
box1 isn't being hovered
hover styles stop applying, the border is readded
basically for the moment the CSS doesn't really evaluate, because as soon as it evaluates the evaluation is invalid. This is exactly what happens in your example. I don't know whether the CSS standard has rules that define how browsers should handle this. If the expected behavior is defined, either FF or Chrome is wrong and you can file a bug after you find out which browser's behavior is wrong. If no expected behavior is defined and the implementation is left open to browsers then both browsers are right.

Related

clickable area of a button that scales down on click

I have an element which is acting as a button with a little Javascript and CSS. I'll strip it down to the most bare example, so you can see the problem. The issue originates from the fact that the element is scaled down when it's clicked. Javascript interprets the clickable area of the button as its scaled down size, not the original size. This occurs in all modern desktop browsers.
Here are the important parts. HTML:
<div id="refresh">more</div>
CSS:
#refresh {
background-color: #FFF;
cursor: pointer;
transition: all 150ms ease-out;
}
#refresh:active {
transform: scale(0.8);
}
JS:
var refreshBtn = document.getElementById("refresh");
function newImg() {
// updates an image elsewhere
}
// an event listener based on
// http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
addEvent(refreshBtn, 'click', newImg);
So my image gets updated when I click on the area occupied by the scaled down button, defined by transform: scale(0.8). If I click outside of that area, in the outer 20% of my button, my JS does not update the image. The proper click transitions occur and the cursor displays correctly as a pointer, but the JS does not count this area as part of the onclick event.
This was covered here, but I find the solution unappealing:
Non-clickable area on transforming anchor
The same solution is offered here:
increasing clickable area of a button
Here's the CSS I used as outlined in those answers:
#refresh:before {
position: absolute;
content: '';
top: -12%;
right: -12%;
left: -12%;
bottom: -12%;
}
These ensure that Javascript now recognizes a bigger clickable area instead of the scaled-down area, but in turn the pointer and the CSS hover effects now react to hovering and clicking well outside the original button element. I consider it an ugly solution. Surely someone else has run into this problem before. Are there any nicer solutions?
Update: Here is a jsfiddle of the situation I've explained: http://jsfiddle.net/cx9ur44e/4/
To solve the issue of the size, you would need to add the click even to a wrapper of the button that will keep the size even if the button is active.
<div id="wrapper>
<div id="refresh">more</div>
</div>

Is it possible to give the cursor z-index?

I am guessing no, but it would be really sweet to be able to set the z-index of the cursor with CSS or Javascript.
Let's say you've got some buttons and you want to add a semi-transparent image on top of the buttons for effect. Or in my current case, some SVG paths that have hover and click actions. If I could set the button or SVG z-index to 0, my cursor's z-index to 1 and the image overlays z-index to 2, that would be pretty sweet! The mouse would be going under the overlay and still be able to click on the buttons. It would be even more spectacular to set the visual z-index (which layer the cursor appears to be), separate from the effective z-index (which layer the cursor actually is). So the cursor could appear to be on top of the overlay, but still be able to click on the buttons underneath.
I have my doubts, but I thought I would check if anyone has heard of someone doing this or something like it.
Since no answer has been accepted, I want offer the right answer.
The pointer-events: none is the solution.
See simple CSS example:
.emotion_message {
pointer-events: none;
background-color: rgb(144,238,144,0.5);
height: 20%;
width: 94%;
position: absolute;
top: 40%;
color: darkgreen;
padding: 1%;
text-align: center;
border-radius: 10px;
margin-left:3%;
margin-right:3%;
}
In this example, I wanted to display a chart, with a static summary box over the top, but I wanted the cursor to interact with the chart underneath. I also added opacity to the background-color, so the user can both see and interact with the submerged element (in this case the chart). Now the user sees the box, but the cursor does not.
Thanks #FabricioMatte for this answer in the comments.
You can play with the cursor:none;
See a related Q: Is it possible to put the mouse cursor behind an element or does the mouse cursor have an z-index?
There is no such thing as a cursor z-index.. what you can do is have a jQuery hover function that gets the object that the cursor is hovering over, which in return allows you to find the objects z-index. So really instead of wanting a cursor to have z-indexes, just have hover states.
Then have custom cursors depending on location. Which as everyone is saying cursor:none would be fun to play with for this. Say you want to go under a alpha block, you could just render a cursor under that alpha block to get the effect of the cursor being under it.

Interactive HTML webpage

EDIT: Thanks for a lot of great examples on how to solve these. I cant decide between who to accept yet, but I will go though all examples and see which I like the most. Great feedback guys! =D
I normally do these kind of things in flash, but this time it has to be compatible with mac, iPads and all those units too.
So, what do I need help with?
I've got a picture, with some "hotspots" on. I want to be able to click any of those hotspots to show some information.
This should be fairly basic and easy to achieve, but since I've never done this in html before I have to ask you guys =)
So, what would be the best way to do this? It have to be compatible with any browser and device, and it doesnt need to be very advanced. If it's possible to add effects to the box (sliding out, fading in, or anything like that) then thats a nice bonus, but not something I need.
Any help would be great!
BREAKDOWN:
I have a background image with some "hotspots" (numbers 1 and 2 in my example). The users should be able to either hover the mouse over any of these or click it to get more information, as seen in picture #2
This is that happens when you hover/click any of these hotspots.
Text and image is displayed inside a nice little info box.
If the user clicks "more information" it will open up even further to display more information if available. Like in this img:
I don't think the Javascript approach is really necessary here. I created a little CSS-only mock-up for you on JSBin.
Basically the point is that you enclose the image in a relatively positioned div, then absolute position the hotspots inside the same div. Inside the hotspots divs you will have the more info elements, showing only on :hover of their parents.
This makes it simple, and far more accessible.
Update: cropping the image equally from both sides
If you want to keep the image centered and still not use any javascript, you could set the required image as a background-image of the container, and setting its background-position parameters to center center.
You would have to make sure that the width of this div is set to the width of your image, and the max-width to 100%, so that when the window gets resized below the image width it stays at the center.
Now, a problem that I encountered here is how to make the hotspots stay center relatively to the image. I solved it this way:
I created a wrapper div for the hotspots with these characteristics:
margin: 0 auto;
position: relative;
width: 0px;
This basically makes sure that the wrapper div finds the center of our image. Then, you would position the hotspots relatively to the top-center position of the image, instead of the top-left as a starting point.
Then you have what you are looking for.
Working demo
Here's another approach, and in my opinion far superior to using a map or excessive JS. Place <div> elements on top of the element with the background-image and have HTML and CSS do the heavy lifting for you.
See it on JSFiddle
HTML
The HTML should seem pretty each enough to understand, we create <div>s with the class hotspot and rely on certain things being present. Namely .text (to show digit), .hover-popup (to show on hover) and .click-popup (which is inside .hover-popup and is shown when clicked).
<div id="hotspot1" class="hotspot">
<div class="text">1</div>
<div class="hover-popup">
I was hovered!
<div class="click-popup">
I was clicked on!
</div>
</div>
</div>
<div id="hotspot2" class="hotspot">
<div class="text">2</div>
<div class="hover-popup">
I was hovered!
<div class="click-popup">
I was clicked on!
</div>
</div>
</div>
CSS
This is where most of the magic happens, see the comments for further explanation.
/* These two position each hotspot */
#hotspot1 {
left:15%; /* we could use px or position right or bottom also */
top:20%;
}
#hotspot2 {
left:35%;
top:25%;
}
/* General styles on the hotspot */
.hotspot {
border-radius:50%;
width:40px;
height:40px;
line-height:40px;
text-align:center;
background-color:#CCC;
position:absolute;
}
.hotspot .text {
width:40px;
height:40px;
}
/* Show the pointer on hover to signify a click event */
.hotspot .text:hover {
cursor:pointer;
}
/* hide them by default and bring them to the front */
.hover-popup,
.click-popup {
display:none;
z-index:1;
}
/* show when clicked */
.hotspot.clicked .click-popup {
display:block;
}
/* show and position when clicked */
.hotspot:hover .hover-popup {
display:block;
position:absolute;
left:100%;
top:0;
width:300px;
background-color:#BBB;
border:1px solid #000;
}
JavaScript (with jQuery)
Unfortunately you're going to have to use some JavaScript for the clicking part as CSS doesn't have a 'clicked' state (outside of hacks with checkboxes). I'm using jQuery because it's dead easy to do what I want.
$(document).ready(function () {
$('.hotspot').click(function () {
$(this).toggleClass('clicked');
});
});
Creating the arrow
Over at css-tricks you can find a tutorial for attaching an arrow to a element using the :before and/or :after pseudo-elements. You can even 'simulate' a border around them by placing the :after element on top of the :before. But yea, lots of resources on how to do this.
You should be able to use the onclick or OnMouseOver event in the map area (define the href as "").
An example using OnMouseOver is here: http://www.omegagrafix.com/mouseover/mousimap.html
Give a class for that image in html (Ex: imgclass). And in javascript(using jquery), build that hover box in html format and bind it to 'mouseover' event of that image.
For example:
function bindhtmltoimage() {
myimg = $('body').find('.imgclass');
divSlot.each(function (index) {
$(this).bind('mouseover', function () {
try {
//position the hover box on image. you can customize the y and x axis to place it left or right.
var x = $(this).offset().left;
var y = $(this).offset().top;
var position = $(window).height() - ($("#divHover").height() + y);
var widthposition = $(window).width() - ($("#divHover").width() + x);
if (position < 0 || widthposition < 0) {
if (position < 0) {
$("#divHover").css({
position: 'absolute',
left: x + 20,
top: y - $("#divHover").height() - 20
});
}
if (widthposition < 0) {
$("#divHover").css({
position: 'absolute',
left: x - $("#divHover").width(),
top: y + 20
});
}
}
//build your html string for that hover box and apply to it.
$('#divHover').html("your Html content for that box goes here");
$('#divHover').show();
//if you want the box dynamically generated. create the html content and append to the dom.
}
catch (e) {
alert(e)
}
});
});
}
it will work fine in desktop and mobile. if you face any problem in touch devices, bind the function to click event instead of 'mouseover'.
Also, for map approach, i strongly recommend SVG instead of images.

CSS and JS - Extend background image beyond div

I'm currently trying to find a workaround to having arrows on Niall Doherty's Coda Slider 2 to highlight the selected tab. Initially I tried doing this with images on the header image, although whilst it looked fine in Safari on my Mac, it wasn't central on other devices (see www.lukekendalldesign.co.uk/pss/productsandservices)
I tried creating this using CSS arrows but that proved rather difficult, so I've found a workaround using a background image, but I've come across yet another problem.
http://cl.ly/HovO (Sorry, I can't upload images - newbie!)
Please refer to the above linked screenshot. The lighter grey triangle that matches the background is part of the header image. The black triangle is positioned using the following CSS code:
.coda-nav ul li a.current {
border-top-right-radius: 10px;
border-top-left-radius: 10px;
color: white;
height: 60px;
z-index: 20000;
background: url(../images/triangle.png) no-repeat 50% 100%;
position: relative;
overflow: visible;
}
What I'm trying to do, is position this black arrow where the grey image arrow is (if that makes sense at all?) How can I do this?
I have tried adding margins and padding, however it extends the grey background and doesn't push the background image black triangle down.
Whilst I have found solutions similar, none seem to apply because the class .current is applied using the following JS:
// If we need a tabbed nav
$('#coda-nav-' + sliderCount + ' a').each(function(z) {
// What happens when a nav link is clicked
$(this).bind("click", function() {
navClicks++;
$(this).addClass('current').parents('ul').find('a').not($(this)).removeClass('current');
offset = - (panelWidth*z);
alterPanelHeight(z);
currentPanel = z + 1;
$('.panel-container', slider).animate({ marginLeft: offset }, settings.slideEaseDuration, settings.slideEaseFunction);
if (!settings.crossLinking) { return false }; // Don't change the URL hash unless cross-linking is specified
});
});
I would very much appreciate any help anyone can offer me on this - as it's a JS issue it's something that's a bit out of my depth! :(
I have tried this in Firefox using fire bug on windows. I think there are 2 problems. The first is that the margin on the ul element should be 167px (the black arrow is not in a nice place in the image (middle is at 232 px did you mean this?).
The the arrow just needs moving down which I did by setting the back ground position to be:
url("../images/triangle.png") no-repeat scroll 50px 60px transparent hope this helps.

Dynamic Background Scrolling

Here's a link to what I'll be referring to.
I'm having some trouble getting the background image to work the way I'd like it to.
I want the background to auto resize based on the width of the window, which it is already doing correctly. If you make your window smaller you'll see the background shrink with it.
Here's the issue. If you make your window wide (short) then the background will resize and go too high so you can't see the top of the background anymore (since the background is bottom positioned).
I want the background to be top position when you are at the top of the page, and as you scroll down it will slowly move to be bottom positioned. Sort of like the effect of an Android phone's background when you move left and right. Of course, keep in mind that I still want the background to auto-resize when you make the window smaller.
html {
background-color: #70d4e3;
height: 100%;
}
body {
height: 100%;
}
.background {
margin-top: 45px;
width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: -9999;
}
.banner {
margin: 0px auto;
width: 991px;
margin-bottom: -9px;
}
.content {
background: url("http://i.imgur.com/daRJl.png") no-repeat scroll center center transparent;
height: 889px;
margin: 0 auto;
width: 869px;
}
.innerContent {
padding: 30px;
}
<img src="http://i.imgur.com/6d5Cm.jpg" alt="" class="background" />
<div class="banner">
<img src="http://i.imgur.com/JptsZ.jpg" alt="" />
</div>
<div class="content">
<div class="innerContent">
testing
</div>
</div>
Maybe some javascript or jquery would be needed to achieve this.
Well, this was fun, thanks!
I hope you don't mind me taking the liberty to use percentages to make my life a little bit easier and possibly the script slightly more robust since I can reliably use floats with percentages.
What I did is make the layout, html and css comply with the rules you need for the bg to be animated properly, they stayed largely the same from what you had.
Then it was just a question of figuring out the calculations needed with the right properties to figure out the percentage you were from the top, the *20 is actually the amount of space 'left' to fill by the background image in percentages (as the background height is 80%).
They I moved the calculations to a function so I could call that on scroll and on window resize, making sure it's initiated on any event that modifies the window somehow...
Didn't do extensive testing but it worked in Chrome and I'm tired :p
I believe this is what you are looking for:
http://jsfiddle.net/sg3s/RSqrw/15/ See edit 2
If you wanted this the other way arround just make the page background start at the top and modify that:
http://jsfiddle.net/sg3s/RSqrw/14/ See edit 2
Edit:
As a bonus, and since I had never actually written jquery script as a 'plugin', I decided to convert this into one. What I came up with should be easy to implement and use!
http://jsfiddle.net/sg3s/RSqrw/52/ See Edit 3
Functionality successfully tested in Chrome, Firefox 3.6, IE9 + compatibility mode
Edit 2:
Reading the question again checking if I did it right I noticed I didn't quite do what you want, so I updated the link in the first edit which gives you a plugin in which you can have several options for the scrolling background. It retains my 'old' interpetation while also doing what you want... Read comments in code for some extra descriptions.
Edit 3:
As I went to work today I was bothered with the fact that my plugin 'try' was a little bloated. And as you mentioned in the comment it didn't quite fit the requirements.
So I rewrote it to only do what you want and not much more, tested in Chrome Firefox, IE9 +compat etc etc.. This script is a lot cleaner.
http://jsfiddle.net/sg3s/vZxHW/
You can chose to make the background stick to the top or bottom if the height fits in the window. Nothing else, but that is already more than enough to do some pretty cool stuff :p
An exact solution: Fiddle: http://jsfiddle.net/srGHE/2/show/
View source
Thanks for the challenge. See below for the solution, which is complying with all requirements, including recommended yet optional (with steps on how to remove these) features. I only show the changed parts of your page, with an explanation after each section (CSS, HTML and JavaScript):
CSS (changes):
html,body{
margin: 0;
height: 100%;
padding: 0;
}
body{
background-color: #70d4e3;
}
#background { /*Previously: .background*/
/*Removed: margin-top: 45px;
No other changes*/
}
#banner /*Previously: .banner; no other changes */
#content /*Previously: .content; no other changes */
#innerContent /*Previously: .innerContent; no other changes */
Explanation of CSS revisions:
margin-top:45px at the background is unnecessary, since you're absolutely positioning the element.
All of the elements which are unlikely to appear more than once should be selected via the id (#) selector. This selector is more specific than the class selector.
HTML (changes):
All of the class attributes have been replaced by id. No other changes have been made. Don't forget to include the JQuery framework, because I've implemented your wishes using JQuery.
JavaScript (new):
Note: I have added a feature which you didn't request, but seems logical. The code will automatically reserve sufficient margin at the left side of the window in order to always display the background. Remove anything between the marked comments if you don't want this feature.
$(document).ready(function(){
//"Static" variables
var background = $("#background");
var marginTop = parseFloat(background.css("margin-top")) || 0;
var bannerWidth = $("#banner").width(); /*Part of auto left-margin */
var extraContWidth = (bannerWidth - $("#content").width())/2; /*Same as above*/
function fixBG(){
var bodyWidth = $("body").width();
var body_bg_width_ratio = bodyWidth/1920;
var bgHeight = body_bg_width_ratio * 926; //Calcs the visible height of BG
var height = $(document).height();
var docHeight = $(window).height();
var difHeight = bgHeight - docHeight;
var scrollDif = $(document).scrollTop() / (height - docHeight) || 0;
/*Start of automatic left-margin*/
var arrowWidth = body_bg_width_ratio * 115; //Arrow width
if(bodyWidth - bannerWidth > arrowWidth*2){
$("body > div").css("margin-left", "auto");
} else {
$("body > #banner").css("margin-left", arrowWidth+"px");
$("body > #content").css("margin-left", (arrowWidth+extraContWidth)+"px");
}
/*End of automatic left-margin*/
if(difHeight > 0){
background.css({top:(-scrollDif*difHeight-marginTop)+"px", bottom:""});
} else {
background.css({top:"", bottom:"0"});
}
}
$(window).resize(fixBG);
$(window).scroll(fixBG);
fixBG();
});
Explanation of the JavaScript code
The size of the background is determined by calculating the ratio of the background and document width. The width property is used, because it's the most reliable method for the calculation.
Then, the height of the viewport, document body and background is calculated. If applicable, the scrolling offset is also calculated, to prepare the movement of the background, if necessary.
Optionally, the code determines whether it's necessary to adjust the left margin (to keep the background visible at a narrow window).
Finally, if the background arrow has a greater height than the document's body, the background is moved accordingly, taking the scrolling position into account. The arrow starts at the top of the document, and will move up as the user scrolls (so that the bottom side of the arrow will be the bottom of the page when the user has fully scrolled down). If it's unnecessary to move the background, because it already suits well, the background will be positioned at the bottom of the page.
When the page has finished loading, this functionality is added to the Resize and scroll events, so that the background is always at the right location.
If you've got any other questions, feel free to ask them.
well, I'm not sure if I understand you and why do you want to do that, but you can try adding 2 backgrounds (see http://www.css3.info/preview/multiple-backgrounds/ ), one with the top bg and another with the bottom bg but I think that if the page is not too long it will cause issues, so the other answer with pure CSS is as follows: first add 3 horizontal divs with 100% width. Top div will have your top bg and its height, middle div will be transparent and auto height and bottom div will have your bottom bg and its height. All divs will have a 0 z-index. Then create a higher z-index div to act as a container and you'll be set. If I understand your question right, that's the close I can think of to achieve that. This being said, I'm pretty sure you can do this with JQuery with way better results
Using jQuery I was able to give you what I think you're asking for:
$(window).scroll(function() {
var h = Math.max($(document).height(), $(window).height());
var bottom = h - $(".background").height() - $(window).height();
$(".background").css("top", (($(window).scrollTop() / h) * bottom) + "px");
});
EDIT: Forgot to account for the way scrollTop reports position.
Or maybe:
.background {
margin-top: 45px;
max-width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: -9999;
max-height: 100%;
}
I reccomend using jQuery Background Parallax
http://www.stevefenton.co.uk/Content/Jquery-Background-Parallax/
The function is as simple as
$("body").backgroundparallax();
Ask if you don't get it to work.
#abney; as i understand your question may that's you want http://jsfiddle.net/sandeep/RSqrw/60/
you need only css for this:
#background {
position: fixed;
width: 100%;
height:100%;
top: 0;
left:0;
z-index: -1;
}
The solution to your issue is a nice little lightweight plugin by Scott Robin. You can get more info, download it, and make your life easier for all of your projects by visiting his project page here.

Categories

Resources