My purpose is to split a certain timeline in divisions.
My only idea of doing it is by doing something like this:
Dividable timeline
Another thing I want to be able to do is resize them by dragging one end to the left or to the right, when this is done, the next one adjusts so they ocupy the same space.
Any division can be split further.
this is what I've come up with but I'm not pleased with it at all:
https://jsfiddle.net/syj6z05v/2/
$(function() {
$( "#resizable1" ).resizable({
containment: "#container"
});
$( "#resizable2" ).resizable({
containment: "#container"
});
$( "#resizable3" ).resizable({
containment: "#container"
});
});
$(function() {
var isDragging = false;
var okay = 0;
var next;
$(".a")
.mousedown(function() {
okay = 1;
isDragging = false;
})
.mousemove(function() {
if (okay == 1){
next = $(".a").next();
var width = 0;
$(this).parent().children().each(function() {
width += $(this).outerWidth( true );
});
next.width(next.parent().width() - (width - next.width()));
isDragging = true;
}
})
.mouseup(function() {
okay = 0;
isDragging = false;
});
});
As discussed in the comments, I think adding movable dividers would be easier, because you don't have to recalculate the position and width of every part.
I've made a little example below. It's not perfect, but I hope it gets you going. You can click to add a divider, right-click to remove one, and you can drag them by holding down the left button.
// Dragging a handle
var dragging = null;
$('.timeline').on('mousedown', '.divider', function(event){
event.stopPropagation();
if (event.which == 1)
dragging = $(this);
});
$('.timeline').on('mousemove', function(event){
if (dragging) {
var left = event.offsetX;
if ($(event.target).hasClass('divider'))
left += dragging.position().left;
dragging
.css('left', left + 'px')
.attr('data-pos', left);
}
});
$('.timeline').on('mouseup mouseenter', function(event){
if ((event.buttons && 1) !== 1)
dragging = null;
});
// Adding a handle
$('.timeline').on('mouseup', function(event){
if (! $(event.target).hasClass('timeline'))
return;
if (event.which == 1)
{
var left = event.offsetX;
var divider =
$('<div>')
.addClass('divider')
.css('left', left + 'px')
.attr('data-pos', left)
.appendTo(this);
}
});
// Removing a handle
$('.timeline').on('mouseup', '.divider', function(event){
if (event.which == 2)
$(this).remove();
});
.timeline {
position: relative;
height: 20px;
border: 1px solid blue;
}
.divider {
position: absolute;
background-color: red;
width: 1px;
height: 100%;
/* Show a cursor to indicate draggability */
cursor: ew-resize;
}
.divider::before {
/* ::Before is used to display the number */
display: block;
position: absolute;
content: attr(data-pos);
height: 100%;
/* Since it is also a capturer for the mouse events of the divider, make sure it extends a bit to the left for easy grabbing. */
padding-left: 6px;
left: -3px;
/* I added background color, so you can see the grabbable area. You would probably want to remove this in production */
background-color: rgba(255, 0, 0, 0.1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click the time line to add markers. Drag the markers to place them on the right spot.
<div class="timeline">
</div>
Related
My implementation,
http://kodhus.com/kodnest/land/PpNFTgp
I am curious, as I am not able for some reason to figure this out, how to get my JavaScript to make my slider behave more natural and smoother, if someone knows, how to, or can make this, feel free. I'd be happy to understand.
JavaScript:
const thumb = document.querySelector('.thumb');
const thumbIndicator = document.querySelector('.thumb .thumb-indicator');
const sliderContainer = document.querySelector('.slider-container');
const trackProgress = document.querySelector('.track-progress');
const sliderContainerStart = sliderContainer.offsetLeft;
const sliderContainerWidth = sliderContainer.offsetWidth;
var translate;
var dragging = false;
var percentage = 14;
document.addEventListener('mousedown', function(e) {
if (e.target.classList.contains('thumb-indicator')) {
dragging = true;
thumbIndicator.classList.add('focus');
}
});
document.addEventListener('mousemove', function(e) {
if (dragging) {
console.log('moving', e)
if (e.clientX < sliderContainerStart) {
translate = 0;
} else if (e.clientX > sliderContainerWidth + sliderContainerStart) {
translate = sliderContainerWidth;
} else {
translate = e.clientX - sliderContainer.offsetLeft;
}
thumb.style.transform = 'translate(-50%) translate(' + translate + 'px)';
trackProgress.style.transform = 'scaleX(' + translate / sliderContainerWidth + ')'
}
});
function setPercentage() {
thumb.style.transform = 'translate(-50%) translate(' + percentage/100 * sliderContainerWidth + 'px)';
trackProgress.style.transform = 'scaleX(' + percentage/100 + ')';
}
function init() {
setPercentage();
}
init();
document.addEventListener('mouseup', function(e) {
dragging = false;
thumbIndicator.classList.remove('focus');
});
EDIT: Is there a way to smoothly and naturally increment by one for every slow move?
Is it possible to make to behave as if, like when one clicks the progress bar so that it jumps there?
The kodhus site is very janky in my browser, so I can't tell if your code lacks responsiveness or whether it's the site itself. I feel that your code is a bit convoluted: translate and width / height are mixed unnecessarily; no need to use a dragging boolean when that information is always stored in the classlist. The following slider performs nicely, and has a few considerations I don't see in yours:
stopPropagation when clicking the .thumb element
drag stops if window loses focus
pointer-events: none; applied to every part of the slider but the .thumb element
let applySliderFeel = (slider, valueChangeCallback=()=>{}) => {
// Now `thumb`, `bar` and `slider` are the elements that concern us
let [ thumb, bar ] = [ '.thumb', '.bar' ].map(v => slider.querySelector(v));
let changed = amt => {
thumb.style.left = `${amt * 100}%`;
bar.style.width = `${amt * 100}%`;
valueChangeCallback(amt);
};
// Pressing down on `thumb` activates dragging
thumb.addEventListener('mousedown', evt => {
thumb.classList.add('active');
evt.preventDefault();
evt.stopPropagation();
});
// Releasing the mouse button (anywhere) deactivates dragging
document.addEventListener('mouseup', evt => thumb.classList.remove('active'));
// If the window loses focus dragging also stops - this can be a very
// nice quality of life improvement!
window.addEventListener('blur', evt => thumb.classList.remove('active'));
// Now we have to act when the mouse moves...
document.addEventListener('mousemove', evt => {
// If the drag isn't active do nothing!
if (!thumb.classList.contains('active')) return;
// Compute `xRelSlider`, which is the mouse position relative to the
// left side of the slider bar. Note that *client*X is compatible with
// getBounding*Client*Rect, and using these two values we can quickly
// get the relative x position.
let { width, left } = slider.getBoundingClientRect();
// Consider mouse x, subtract left offset of slider, and subtract half
// the width of the thumb (so drags position the center of the thumb,
// not its left side):
let xRelSlider = evt.clientX - left - (thumb.getBoundingClientRect().width >> 1);
// Clamp `xRelSlider` between 0 and the slider's width
if (xRelSlider < 0) xRelSlider = 0;
if (xRelSlider > width) xRelSlider = width;
// Apply styling (using percents is more robust!)
changed(xRelSlider / width);
evt.preventDefault();
evt.stopPropagation();
});
slider.addEventListener('mousedown', evt => {
let { width, left } = slider.getBoundingClientRect();
// Clicking the slider also activates a drag
thumb.classList.add('active');
// Consider mouse x, subtract left offset of slider, and subtract half
// the width of the thumb (so drags position the center of the thumb,
// not its left side):
let xRelSlider = evt.clientX - left - (thumb.getBoundingClientRect().width >> 1);
// Apply styling (using percents is more robust!)
changed(xRelSlider / width);
evt.preventDefault();
evt.stopPropagation();
});
changed(0);
};
let valElem = document.querySelector('.value');
applySliderFeel(document.querySelector('.slider'), amt => valElem.innerHTML = amt.toFixed(3));
.slider {
position: absolute;
width: 80%; height: 4px; background-color: rgba(0, 0, 0, 0.3);
left: 10%; top: 50%; margin-top: -2px;
}
.slider > .bar {
position: absolute;
left: 0; top: 0; width: 0; height: 100%;
background-color: #000;
pointer-events: none;
}
.slider > .thumb {
position: absolute;
width: 20px; height: 20px; background-color: #000; border-radius: 100%;
left: 0; top: 50%; margin-top: -10px;
}
.slider > .thumb.active {
box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.5);
}
<div class="slider">
<div class="bar"></div>
<div class="thumb"></div>
</div>
<div class="value"></div>
Found a lightway draggable function. How can I lock the draggable black block in parent area? Do i need to do a width and height to limit black block area?
online sample http://jsfiddle.net/zqYZG/
.drag {
width: 100px;
height: 100px;
background-color: #000;
}
.box{
width: 500px;
height: 400px;
background-color:red;
}
jQuery
(function($) {
$.fn.draggable = function(options) {
var $handle = this,
$draggable = this;
options = $.extend({}, {
handle: null,
cursor: 'move'
}, options);
if( options.handle ) {
$handle = $(options.handle);
}
$handle
.css('cursor', options.cursor)
.on("mousedown", function(e) {
var x = $draggable.offset().left - e.pageX,
y = $draggable.offset().top - e.pageY,a
z = $draggable.css('z-index');
$draggable.css('z-index', 100000);
$(document.documentElement)
.on('mousemove.draggable', function(e) {
$draggable.offset({
left: x + e.pageX,
top: y + e.pageY
});
})
.one('mouseup', function() {
$(this).off('mousemove.draggable');
$draggable.css('z-index', z);
});
// disable selection
e.preventDefault();
});
};
})(jQuery);
$('.drag').draggable();
Here is a simple way to do it, using the getBoundingClientRect() function: updated JSFiddle
This just constrains the l and t variables from your original code, to be within the parent node's dimensions.
See containment option!
use :
$( ".selector" ).draggable({ containment: "parent" });
It's easy to keep a column in my layout fixed so it's always visible, even when the user scrolls down.
It's also easy to only move the column down the page when the page is scrolled down far enough for it to be out of the viewport so it's anchored before scrolling starts.
My problem is, I have left hand column that is taller than the average window so you need to be able to scroll down to see all the content (controls) in the left column but at the same time when you scroll up you want to see the top of the controls again.
Here's a visual of what I want to accomplish:
So the left column is always occupying 100% of the height of the window but as the user scrolls down they can see the bottom of the div, and when they start to scroll up the scrolls up until it reaches the top of the window again. So no matter how far they scroll the page, the top of the div is always nearby.
Is there some jQuery magic to make this happen?
Did you mean something like this? (Demo)
var sidebar = document.getElementById('sidebar');
var sidebarScroll = 0;
var lastScroll = 0;
var topMargin = sidebar.offsetTop;
sidebar.style.bottom = 'auto';
function update() {
var delta = window.scrollY - lastScroll;
sidebarScroll += delta;
lastScroll = window.scrollY;
if(sidebarScroll < 0) {
sidebarScroll = 0;
} else if(sidebarScroll > sidebar.scrollHeight - window.innerHeight + topMargin * 2) {
sidebarScroll = sidebar.scrollHeight - window.innerHeight + topMargin * 2;
}
sidebar.style.marginTop = -sidebarScroll + 'px';
}
document.addEventListener('scroll', update);
window.addEventListener('resize', update);
#sidebar {
background-color: #003;
bottom: 1em;
color: white;
left: 1%;
overflow: auto;
padding: 1em;
position: fixed;
right: 80%;
top: 1em;
}
body {
line-height: 1.6;
margin: 1em;
margin-left: 21%;
}
It almost degrades gracefully, too…
I made a fiddle for you, hope this helps you out abit.
I detect scroll up or scroll down, and set the fixed position accordion to the direction.
http://jsfiddle.net/8eruY/
CSS
aside {
position:fixed;
height:140%;
background-color:red;
width:100px;
top:20px;
left:20px;
}
Javascript
//Detect user scroll down or scroll up in jQuery
var mousewheelevt = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel" //FF doesn't recognize mousewheel as of FF3.x
$('html').bind(mousewheelevt, function(e){
var evt = window.event || e //equalize event object
evt = evt.originalEvent ? evt.originalEvent : evt; //convert to originalEvent if possible
var delta = evt.detail ? evt.detail*(-40) : evt.wheelDelta //check for detail first, because it is used by Opera and FF
if(delta > 0) {
$('aside').css('top', '20px');
$('aside').css('bottom', 'auto');
}
else{
$('aside').css('bottom', '20px');
$('aside').css('top', 'auto');
}
});
http://jsfiddle.net/KCrFe/
or this:
.top-aligned {
position: fixed;
top: 10px;
}
with
var scrollPos
$(window).scroll(function(event){
var pos = $(this).scrollTop();
if ( pos < scrollPos){
$('.sidebar').addClass('top-aligned');
} else {
$('.sidebar').removeClass('top-aligned');
}
scrollPos = pos;
});
I want to make a very simple function in my jQuery script. When the finger/cursor touches/clicks on the screen, I want the pages to slide horizontally following the movements of the finger/cursor. I know there is a lot of plugins created by so many people, but I really don't need everybody else's solutions. The image is a visual view of how my HTML looks like. it is really simple.
The jQuery sciprt is obviously not correct, but I hope it would give you an idea about the simple function I need. I don't extra classes or fade-functions or anything.
$(document).live('touchmove' or 'mousemove', function() {
$('div[class=page_*], div[class=page_^]').[follow movements horizontally, and auto align to nearest edge when let go.];
});
Also I want to be able to do the same with one big div, so probably the width-variable of the element moving should be equal to $(window).width();. Actually I think that would be the best idea. I can always put more content inside the big div and make it larger, so keep it with that. It should be more simple to do and to focus on one element only.
So, here is my solution. I've made some changes so that now you can have more than 3 pages.
Also, I've defined a variable named threshold set to the half of a page. If you want to have a threshold bigger or smaller than the hakf of the page you will have to make some more changes.
HTML CODE:
<div class="container">
<div class="wrap">
<div class="page page1"></div>
<div class="page page2"></div>
<div class="page page3"></div>
<div class="page page4"></div>
</div>
</div>
CSS CODE:
.container, .page, .wrap {
width: 300px;
height: 400px;
}
.container {
background: #efefef;
box-shadow: 0px 0px 10px black;
overflow: hidden;
position: relative;
margin: 5px auto;
}
.wrap {
width: 1200px;
position: absolute;
top: 0;
left: 0;
}
.page {
float: left;
display: block;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
}
.page1 {
background: yellow;
}
.page2 {
background: green;
}
.page3 {
background: blue;
}
.page4 {
background: red;
}
As for the CSS code keep in mind that if you want to change the page size you will also have to change the container and wrap size.
JS CODE:
var mouseDown = false, right;
var xi, xf, leftX = 0;
var nPages = $(".page").size();
var pageSize = $(".page").width();
var threshold = pageSize/2;
var currentPage = 0;
$(".container").on("mousedown", function (e) {
mouseDown = true;
xi = e.pageX;
});
$(".container").on("mouseup", function (e) {
if (mouseDown) {
mouseDown = false;
xf = e.pageX;
leftX = parseInt($(".wrap").css("left").split("px")[0]);
if ((e.pageX - xi) < -threshold || (e.pageX - xi) > threshold) {
setFocusedPage();
} else {
restore();
}
}
});
$(".container").on("mouseleave", function (e) {
if (mouseDown) {
mouseDown = false;
xf = e.pageX;
leftX = parseInt($(".wrap").css("left").split("px")[0]);
if ((e.pageX - xi) < -threshold || (e.pageX - xi) > threshold) {
setFocusedPage();
} else {
restore();
}
}
});
$(".container").on("mousemove", function (e) {
if (mouseDown) {
$(".wrap").css({
"left": (leftX + (e.pageX - xi))
});
right = ((e.pageX - xi) < 0) ? true : false;
}
});
function restore() {
$(".wrap").stop().animate({
"left": -(currentPage * pageSize)
}, 200, function () {
leftX = parseInt($(".wrap").css("left").split("px")[0]);
});
}
function setFocusedPage() {
if (leftX >= (-threshold)) { // First Page
currentPage = 0;
} else if (leftX < (-threshold) && leftX >= (-(nPages + 1) * threshold)) { // Second to N-1 Page
(right) ? currentPage++ : currentPage--;
} else if (leftX < -((nPages + 1) * threshold)) { // Third Page
currentPage = nPages - 1;
}
$(".wrap").stop().animate({
"left": -(currentPage * pageSize)
}, 200, function () {
leftX = parseInt($(".wrap").css("left").split("px")[0]);
});
}
Remember here that if you want a different threshold you will have to make some changes especially in the setFocusedPage() function.
Here is my last DEMO
I was wondering if it is possible to set background-color with help of mouse coordinates.
What i have is:
I have a DIV-A which is draggable and some other divs which are droppable.
What i need is :
I need to highlight other divs on my page which are droppable, whenever my DIV-A passes over them. What i have is mouse coordinates, is it possible to apply css on the bases of mouse coordinates using jquery.
Something like the following may work. You will probably need to deal with window's scrollLeft and scrollTop to get it perfect. You will probably want to throttle and memoize (if the drop positions don't change) it too.
Also, some more performance can be tweaked out of it by caching offset(), only binding mousemove when needed, and by tweaking the each loop to utilize an optimized loop (e.g. for(var i=droppables.length;i>-1;){var self = droppables.eq(--i);...}).
Also note that this will only change the color of the divs when the MOUSE passes over them...not necessarily when the draggable passes over them...this makes things a little more complicate but the function below should send you in the right direction.
$(document).mousemove(function(e){
// this should be throttled...
var x = e.pageX,
y = e.pageY;
// this loop could be optimized...
$("div.droppables").each(function(){
// these vars could be memoized...
var self = $(this),
divL = self.offset().left,
divT = self.offset().top,
divR = self.width() + divL,
divB = self.height() + divT;
// if the MOUSE coords are between the droppable's coords
// change the background color
if(x >= divL && x <= divR && y >= divT && y <= divB){
self.css("background", "red");
}
else{
// reset the background color
self.css("background", "");
}
});
});
I posted a demo for you here. Basically this cycles through each droppable position, so if you have a lot of them, it could really slow down mouse movement.
Oh, and I added two variables you can adjust if you want to increase the proximity to the droppable. Adjust the xmargin and ymargin variables as desired.
CSS
.draggable { width: 90px; height: 90px; padding: 0.5em; position: relative; top: 0; left: 0; z-index: 2; }
.droppable { width: 120px; height: 120px; padding: 0.5em; position: absolute; z-index: 1; }
#drop1 { top: 150px; left: 300px; }
#drop2 { top: 400px; left: 100px; }
HTML
<div class="draggable ui-widget-content">
<p>Drag me to my target</p>
</div>
<div id="drop1" class="droppable ui-widget-header">
<p>Drop here</p>
</div>
<div id="drop2" class="droppable ui-widget-header">
<p>Drop here</p>
</div>
Script
$(function(){
var xmargin = 10,
ymargin = 10,
drag = $('.draggable'),
drop = $('.droppable'),
dgw = drag.outerWidth() + xmargin,
dgh = drag.outerHeight() + ymargin,
pos = [];
drop
.droppable({
//hoverClass: 'ui-state-active',
drop: function(event, ui) {
$(this).addClass('ui-state-highlight').find('p').html('Dropped!');
}
})
// set up droppable coordinates array (left, top, right, bottom) for each element
.each(function(i){
var dropzone = drop.eq(i);
var l = dropzone.position().left,
t = dropzone.position().top,
r = l + dropzone.outerWidth() + xmargin,
b = t + dropzone.outerHeight() + ymargin;
pos.push([l,t,r,b]);
});
drag
.draggable()
// bind to drag event, or this could be placed inside the draggable function
.bind( "drag", function(event,ui){
var l = ui.offset.left,
t = ui.offset.top;
// cycle through each droppable and compare current postion to droppable array
drop.each(function(i){
if ( ( l + dgw ) > pos[i][0] && l < pos[i][2] && ( t + dgh ) > pos[i][1] && t < pos[i][3] ) {
$(this).addClass('ui-state-active');
} else {
$(this).removeClass('ui-state-active');
}
});
});
});
Have a look at the "Visual feedback" sample over at
jQuery UI, and as gmcalab mentioned, not having IDs is not an issue if you just use a class as the selector. Sorry if I'm not reading this correctly.
Declare selector and selector2 to whatever you want...
$(selector).mousemove(function(event) {
// Set some bounds, these are arbitrary here not sure what sort of area your looking for...
var lowerXBound= 0,
upperXBound = 100,
lowerYBound = 0,
upperYBound = 100,
currentX = event.pageX,
currentY = event.pageY;
var color = currentX > lowerXBound && currentX < upperXBound && currentY > lowerYBound && currentY < upperYBound ? 'red' : 'green';
$(selector2).css('background-color', color);
});
You can use .hover() for this, so when the mouse is over the div, change it's background colour:
$("yourdiv").hover(function () {
$(this).css("background-color", "#ff0000");
},
function () {
$(this).css("background-color", "#ffffff");
});