Adjustable page division boundary - javascript

I have two columns in my HTML page.
<div id="content">
<div id="left"></div>
<div id="right"></div>
</div>
Each of them occupies half of the page
#content {
height: 100%;
}
#left, #right {
float: left;
width: 50%;
height: 100%;
overflow: auto;
}
I'd like the boundary between left and right halves to be adjustable by the user. That is, the user can move the boundary to the left or to the right as he/she browses the page. Is it possible to do that somehow?

Yes, but it requires JavaScript. To apply it, you could of course just set the width of each of the sides:
var leftPercent = 50;
function updateDivision() {
document.getElementById('left').style.width = leftPercent + '%';
document.getElementById('right').style.width = (100 - leftPercent) + '%';
}
Now you can adjust the division with, say leftPercent = 50; updateDivision(), but the user isn't going to do that. There are multiple different ways you could present this to the user. Probably the best-suited way would be a little line in the middle they could drag. For this, you could use a little CSS for the positioning:
#content {
position: relative;
}
#divider {
position: absolute;
/* left to be set by JavaScript */
width: 1px;
top: 0;
bottom: 0;
background: black;
cursor: col-resize;
/* feel free to customize this, of course */
}
And then make sure you've got a div with an id of divider in content and update updateDivision to also update the left of divider:
document.getElementById('left').style.left = leftPercent + '%';
Then you just need a little logic to handle the dragging. (Here, I've put all of the elements into appropriately-named variables):
divider.addEventListener('mousedown', function(e) {
e.preventDefault();
var lastX = e.pageX;
document.documentElement.addEventListener('mousemove', moveHandler, true);
document.documentElement.addEventListener('mouseup', upHandler, true);
function moveHandler(e) {
e.preventDefault();
e.stopPropagation();
var deltaX = e.pageX - lastX;
lastX = e.pageX;
leftPercent += deltaX / parseFloat(document.defaultView.getComputedStyle(content).width) * 100;
updateDivision();
}
function upHandler(e) {
e.preventDefault();
e.stopPropagation();
document.documentElement.removeEventListener('mousemove', moveHandler, true);
document.documentElement.removeEventListener('mouseup', upHandler, true);
}
}, false);
You should be able to read it to see how it works, but in short: It listens for when someone presses on the divider. When they do, it'll attach listeners to the page for when they move their mouse. When they do, it updates the variable and calls updateDivision to update the styles. When eventually it gets a mouseup, it stops listening on the page.
As a further improvement, you could make every element have an appropriate cursor style while dragging so your cursor doesn't flash while dragging it.
Try it out.

There's nothing in the divisions so nothing will happen. It's like writing:
<h1></h1>
And changing the CSS for h1 and expecting something to be there

Related

Infinite moving text with scrolling

I want make a moving infinite text like this text(We are trusted by over 28,000...) in this link . I already tried something but I have not reached exactly what I want. It is close but not infinite.
<h1 class="deneme display-1 " id="movingtext">We are trusted by over 28,000 clients to power stunning websites.We are trusted by over 28,000 clients to power stunning websites.</h1>
window.addEventListener('scroll', () => {
var elemen = document.getElementById("movingtext");
elemen.style = "left:-450px"
var rect = elemen.getBoundingClientRect();
var rect1 = rect.left;
var scrolled = window.scrollY;
var deg = (rect1 + scrolled) / 1.8;
elemen.style = "left:" + deg + "px";
console.log(rect.left, rect.right);
})
As you can see in the following screenshots this is not infinite either:
first row with "We are trusted by over 28,000..."
and second row
It's just that on normal screen you can't reach the end of the element.
You can accomplish something similar by adding an listener on scroll and transforming the element you want to be moved retative to window.pageYOffset value.
Something like:
window.addEventListener('scroll', () => {
const movingtext = document.getElementById("movingtext");
const scrolled = window.pageYOffset;
//feel free to play with this value to change the speed of the transform ( the `* 3` part)
const left = scrolled * 3;
movingtext.style.transform = `translate3d(-${left}px, 0px, 0px)`;
})
.wrapper {
height: 300vh;
padding-top: 100vh;
max-width: 100vw;
overflow: hidden;
}
.infinite-text {
display: inline-block;
overflow: hidden;
white-space: nowrap;
}
<div class="wrapper">
<h1 class="infinite-text" id="movingtext">We are trusted by over 28,000 clients to power stunning websites.We are trusted by over 28,000 clients to power stunning websites.</h1>
</div>
Please note that this is a draft. You might want to play with sizes and speed of transform or other attributes.
Please note that for this example the container has max-width: 100vw; and overflow: hidden (so it won't display a scrollbar for the element that is bigger than the screen), and the element itself has white-space: nowrap;

JQuery Draggable with Touch Punch stops vertical scroll

I am using JQuery Draggable function and Touch Punch to produce a list of horizontal sliders that can be scrolled by clicking and dragging. It works great in touch and click devices. The problem I am facing is that if I try to scroll up or down in touch devices, it doesn't work.
I have looked through SO and found that removing "event.preventDefault" from TouchPunch allows vertical scrolling, the problem with this fix is, that it only works on some devices, and not all.
I am wondering if anyone has any other solutions, or alternative way of producing the same horizontal sliders that work on both touch and click events.
Here is Example code (JQuery Draggable):
$(function() {
var slides = $('#list1 ul').children().length;
var slideWidth = $('#list1').width();
var min = 0;
var max = -((slides - 1) * slideWidth);
$("#list1 ul").width(slides * slideWidth).draggable({
axis: 'x',
drag: function(event, ui) {
if (ui.position.left > min) ui.position.left = min;
if (ui.position.left < max) ui.position.left = max;
}
});
$("#list2 ul").width(slides * slideWidth).draggable({
axis: 'x',
drag: function(event, ui) {
if (ui.position.left > min) ui.position.left = min;
if (ui.position.left < max) ui.position.left = max;
}
});
});
#list1 {
position: relative;
height: 16em;
width: 100%;
text-align: middle;
white-space: nowrap;
overflow-x: hidden;
overflow-y: hidden;
}
#list1 .floating-box {
margin: auto;
display: inline-block;
width: 15em;
height: 13.5em;
margin: 0.1em;
border: 0.2em solid black;
background-color: white;
}
#list2 {
position: relative;
height: 16em;
width: 100%;
text-align: middle;
white-space: nowrap;
overflow-x: hidden;
overflow-y: hidden;
}
#lis2 .floating-box {
margin: auto;
display: inline-block;
width: 15em;
height: 13.5em;
margin: 0.1em;
border: 0.2em solid black;
background-color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="list1">
<ul>
<p>One</p>
<div class="floating-box">Floating box</div>
<div class="floating-box">Floating box</div>
<div class="floating-box">Floating box</div>
</ul>
</div>
<div id="list2">
<ul>
<p>Two</p>
<div class="floating-box">Floating box</div>
<div class="floating-box">Floating box</div>
<div class="floating-box">Floating box</div>
</ul>
</div>
If I touch list1 or list2 div and try to scroll up or down, it doesn't recognize the movement. Any help or direction would be appreciated.
EDIT
Based on the idea to determine theswipe direction based on the pageY propertie of touchemove.
I think it's a good idea to avoid the annoying double tap (see first answer below) on the long run.
There is still a compromise on it, but it is much more reasonable.
I tried many things... The best result I got is when I gave up on simulating a scroll on touchmove.
So here is the way to I now use each of the three touch events:
touchstart gets the initial variables like: window scrollTop and pageY.
touchmove determines the swipe direction and gets the last pageY.
touchend does the math as to were the page should scrollTo.
For the cuteness, I've put that result value in an .animate().
I was pleasantly surprised to see that it compensates quite really smoothly the fact that the page scrolls only on touchend.
I think that very few users will notice it ;).
Since "Touch-Punch" is working by default for horizontal swipes, the "compromise" only affects the vertical scroll.
Here is the code:
And a live link to try it on a touche-enabled device.
$(function() {
var slides = $('#list1 ul').children().length;
var slideWidth = $('#list1').width();
var min = 0;
var max = -((slides - 1) * slideWidth);
$(".draggable").width(slides * slideWidth).draggable({
axis: 'x',
drag: function(event, ui) {
if (ui.position.left > min) ui.position.left = min;
if (ui.position.left < max) ui.position.left = max;
}
});
var startTouchX=0;
var startTouchY=0;
var actualPosX;
var actualPosY;
var eventCounter=0;
var directionDetermined=false;
var direction;
var thisTouchX;
var thisTouchY;
var lastTouchY;
$(document).on("touchstart", function(e) {
// Actual document position
actualPosX = $(document).scrollLeft();
actualPosY = $(document).scrollTop();
// StartTouches
startTouchX = parseInt(e.originalEvent.touches[0].pageX);
startTouchY = parseInt(e.originalEvent.touches[0].pageY);
});
$(document).on("touchmove", function(e) {
// Arbitrary considering ONLY the fourth event...
// Touchmove fires way too many times!
// We only need to determine the main direction ONCE.
// This prevents an "s" swipe from messing this code.
eventCounter++;
if(eventCounter==4 && !directionDetermined){
thisTouchX = parseInt(e.originalEvent.touches[0].pageX);
thisTouchY = parseInt(e.originalEvent.touches[0].pageY);
if ( (Math.abs(thisTouchX - startTouchX)) / Math.abs(thisTouchY - startTouchY) > 1){ //check swipe direction
// HORIZONTAL
$("#debug").html("HORIZONTAL");
directionDetermined=true;
// NO NEED here. This is re-enabled on touchend, if it has been disabled.
//$(".draggable").draggable('enable');
}
else{
// VERTICAL
$("#debug").html("VERTICAL");
directionDetermined=true;
direction="vertical";
$(".draggable").draggable('disable'); // Disable draggable.
}
}
// Getting all the Y touches...
// The "last" value will be used on touchend.
lastTouchY = parseInt(e.originalEvent.touches[0].pageY);
//$("#debug").html(lastTouchY);
});
$(document).on("touchend", function(e) {
if(direction=="vertical"){
//$("#debug").html(lastTouchY);
var thisMoveY = -parseInt(lastTouchY - startTouchY);
//$("#debug").html(thisMoveY);
var newPosY = (actualPosY + thisMoveY);
//$("#debug").html(actualPosX+ " " +newPosY);
//window.scrollTo(actualPosX, newPosY);
$("html,body").animate({ scrollTop: newPosY },400);
}
// Reset everything for a future swipe.
startTouchX=0;
startTouchY=0;
eventCounter=0;
directionDetermined=false;
direction="";
$("#debug").html("");
// Re-enable draggable.
$(".draggable").draggable('enable');
});
});
First answer, using a "double-tap" to switch direction.
First, the Touch Punch website states that it's basically a jQuery-UI hack to handle some cases actually unhandled by jQuery-UI...
And that it is possible to find cases where Touch Punch fails.
Your issue was reported to the Touch Punch developpers here.
As an answer to that (in my words here), they said that it isn't really bug or an issue...
But a usage "conflict" on two different "wished" actions that are using the same touch events.
Sometimes to scroll the page, and sometimes to drag an element.
As a solution hint, they posted this Fiddle.
It suggests to find a way to disable draggable when needed.
But in this solution, the scrollable section is within the draggable element.
Which is not your case.
And you use almost all the mobile screen space for your draggable elements, so there is not enougth space left to trigger a draggable("disable") around them.
So... I had this idea, which I hope will help.
What if you'd find an elegant way to inform your users that a "double-tap" changes the movement orientation.
Here, I suggest a quite simple "double arrow" showing the movement direction.
Maybe you'll find something better.
This sure is a little compromise user experience, to ask them to double tap...
But if your layout really needs it, maybe it's ok.
So here, I reproduced your initial issue.
And here is the fix that I suggest.
I only tryed it on a Samsung Galaxy S3, but should work on every touch device.
$(function() {
var slides = $('#list1 ul').children().length;
var slideWidth = $('#list1').width();
var min = 0;
var max = -((slides - 1) * slideWidth);
$(".draggable").width(slides * slideWidth).draggable({
axis: 'x',
drag: function(event, ui) {
if (ui.position.left > min) ui.position.left = min;
if (ui.position.left < max) ui.position.left = max;
}
});
// Flag
var draggableEnabled=true;
// Find the doubletap position (to show a nice double arrow)
var tapPosition=[];
$(document).on("touchstart",function(e){
tapPosition[0] = parseInt(e.touches[0].pageX) - $(document).scrollLeft() - ($("#arrows img").width()/2);
tapPosition[1] = parseInt(e.touches[0].pageY) - $(document).scrollTop() - ($("#arrows img").width()/2);
});
Hammer(document).on("doubletap", function() {
//alert("Double tap");
draggableEnabled = !draggableEnabled; // Toggle
if(!draggableEnabled){
$(".draggable").draggable('disable'); // Disables draggable (and touch Punch)
$("#arrows img").css({
"transform":"rotate(90deg)", // Nice vertical double arrow
"top":tapPosition[1],
left:tapPosition[0]
}).fadeIn(600, function(){
$(this).fadeOut(600);
});
}else{
$(".draggable").draggable('enable'); // Enables draggable (and touch Punch)
$("#arrows img").css({
"transform":"rotate(0deg)", // Nice horizontal double arrow
"top":tapPosition[1],
left:tapPosition[0]
}).fadeIn(600, function(){
$(this).fadeOut(600);
});
}
});
});
Notice that it uses the Hammer.js (CDN) to detect the double tap.
And some extra CSS for the double arrow.
#arrows img{
width: 60vw;
height: 60vw;
position: fixed;
top:calc( 50vh - 60vw );
left:calc( 50vh - 60vw );
z-index:1000;
display:none;
}
This is the closest I have come to it, I wonder if someone could refine this code:
$(watchlist).on("touchstart", function(e) {
touchY = e.originalEvent.touches[0].pageY;
touchX = e.originalEvent.touches[0].pageX;
});
$(watchlist).on("touchmove", function(e) {
var fTouchY = e.originalEvent.touches[0].pageY;
var fTouchX = e.originalEvent.touches[0].pageX;
if ((Math.abs(fTouchX - touchX)) / Math.abs(fTouchY - touchY) > 1){ //check swipe direction
$("#watchlist ul").draggable( 'enable'); //if swipe is horizontal
}
else{
$("#watchlist ul").draggable( 'disable'); //if swipe is horizontal
}
});
The problem with this code is, that it deactivates the draggable function only after the touch move has been finished rather than during. If anyone could modify this code so that the draggable function is deactivated during as soon as the condition is met, during touchmove, rather than after, I would give them the bounty.

jQuery doesn't show and hide the header

I'm trying to make a header that appears at a certain place of the page.
So what I'm doing is checking the scroll to top of the page and the top offset of the element after which the header should appear. If the scrollTop is greater than offset the header is shown, otherwise it disappears.
But! When I scroll to the place, the header position is constantly switching between top: -13% and top: -12.999998%. After some time it finally shows the header but it never disappears.
What am I doing wrong?!
JSFiddle: https://jsfiddle.net/5k5s016f/
Well, i think the problem is that the .animate() functions are running constantly, causing the animations to "restart" before its ends.
It is not the most beautiful solution, but just adding a flag that controls the execution of the functions and a timeout to run the handler less frequently solves the problem.
https://jsfiddle.net/5k5s016f/2/
var visible = false;
$(window).scroll(function() {
setTimeout(function(){
var height = $(window).scrollTop();
var $page2 = $("#page2");
var offset = $page2.offset().top;
if (height > offset) {
if (visible) {
return;
}
visible = true;
$(".floating-header").show().animate({
top: 0
});
} else {
if (!visible) {
return;
}
visible = false;
$(".floating-header").animate({
top: "-13%"
});
}
}, 200)
});
The issue you are seeing is because each time a scroll event gets called animation queues up. If you wait long enough, you can see that the animation to set top to 0 actually works.
You can use the stop() function to stop all animation before attempting to run another one.
Something like this
if (height > offset) {
$(".floating-header").stop().show().animate({
top: "0"
}, 700);
} else {
$(".floating-header").stop().animate({
top: "-13%"
}, 700);
}
A couple of improvements I can suggest are
Debounce the scroll event handler
Check the current state of the header before queuing animation. i.e. do not try to hide it if it is already hidden and vice versa
Your logic is all messed up. Basically, you want to make sure that you are only animating when you absolutely need to - no more, no less. And since scroll events happen hundreds of times... constantly rapid firing as the user scrolls... you want to make sure you are doing the least amount of work possible during each scroll event. This especially means that you don't want to be querying the DOM on every scroll event if you don't have to (ps. $('selector') is a dom query). Take a look at this fiddle:
https://jsfiddle.net/5k5s016f/6/
Looks like I'm last to the party due to interruptions, but since I wrote it up I'll post the answer FWIW.
jsFiddle Demo
You need to debounce your code. Here is a simple system, but studing Ben Alman's explanation/examples is also recommended.
var $m1 = $('#m1'), $m2 = $('#m2'); //TESTING ONLY
var $win = $(window), $page2 = $("#page2"), $hdr=$(".floating-header");
var $offset = $page2.offset().top;
var hvis = false, curpos;
$win.scroll(function() {
curpos = $win.scrollTop();
$m1.html(curpos); //TESTING ONLY
$m2.html($offset);//TESTING ONLY
if ( curpos > $offset ) {
if ( !hvis ){
hvis = true;
//$m1.html(curpos);
$hdr.finish().animate({
top: "0"
}, 700);
}
} else {
if ( hvis ){
$hdr.finish().animate({
top: "-60px"
}, 700);
hvis = false;
}
}
});
html,
body {
height: 100vh;
width: 100vw;
}
#page1,
#page2,
#page3 {
height: 100%;
width: 100%;
background-color: #fff;
}
.floating-header {
position: fixed;
top: -60px;
background-color: #000;
width: 100%;
height: 60px;
}
.msg{position:fixed;bottom:10px;height:30px;width:80px;text-align:center;}
.msg{padding-top:10px;}
#m1 {left:3px; border:1px solid orange;background:wheat;}
#m2 {right:3px;border:1px solid green; background:palegreen;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<header class="floating-header">Header</header>
<div id="page1">
<p>Page1</p>
</div>
<div id="page2">
<p>Page2</p>
</div>
<div id="page3">
<p>Page3</p>
</div>
<div id="m1" class="msg"></div>
<div id="m2" class="msg"></div>

How do I script such that my panoramic image can be panned left and right up to its edges?

Say my image (panoramic) is 10,000 pixels long, but I want to be able to view only 1000 pixels wide at a time, and in order to view more of it, I can just hover my mouse either left or right, and then the image will move accordingly please? If possible, a simple script on HTML? (I'm not sure how to use Javascript or CSS, but if it needs to come down to that, do guide me step by step?
Thank you.
Here is a simple JQuery which you can use to scroll the whole page when hovering over either the left or right;
Example - https://jsfiddle.net/38da9pca/
Need any help implementing it just leave a comment and I will try and help you.
$(function() {
$('#right').on('mouseenter', rscroll);
$('#left').on('mouseenter', lscroll);
$('#right, #left').on('mouseleave', function() {
$('body').stop();
});
function rscroll() {
$('body').animate({
scrollLeft: '+=25'
}, 10, rscroll);
}
function lscroll() {
$('body').animate({
scrollLeft: '-=25'
}, 10, lscroll);
}
});
Edit (Scroll Image Only)
Example - https://jsfiddle.net/38da9pca/1/
I have change it so the lscroll and the rscroll will effect the id of image instead of the body and change it from scrollLeft to left, and that way it will move the images scroll. Dont forgot to change the $('body').stop(); to $('#bg').stop(); or it will never stop scrolling
$(function() {
$('#right').on('mouseenter', rscroll);
$('#left').on('mouseenter', lscroll);
$('#right, #left').on('mouseleave', function() {
$('#bg').stop();
});
function rscroll() {
$('#bg').animate({
left: '-=25'
}, 10, rscroll);
}
function lscroll() {
$('#bg').animate({
left: '+=25'
}, 10, lscroll);
}
});
Here's one. It's using jquery (javascript library). You would have to add it in order to make it work.
Example:
http://www.gayadesign.com/scripts/jqueryphotonav/
Tutorial:
https://blog.gaya.ninja/articles/jquery-convertion-panoramic-photoviewer-in-javascript/
Something like this?
var imgView = document.querySelectorAll('.img-view')[0];
var img = imgView.querySelectorAll('img')[0];
imgView.onmousemove = function(e) {
var x = e.clientX;
var px = ((x / this.offsetWidth) * 100);
var ix = ((px / 100) * img.offsetWidth);
img.style.left = '-' + (ix - this.offsetWidth) + 'px';
}
.img-view {
width: 256px;
height: 160px;
overflow: hidden;
position: relative;
border: 1px solid #ddd;
}
.img-view img {
height: 100%;
position: relative;
top: 0;
left: 0;
}
<div class="img-view">
<img src="http://panoramalove.com/wp-content/uploads/2013/01/nighttime-panoramic-view-of-hong-kong-island-from-the-avenue-of-stars-in-tsim-sha-tsui.jpg">
</div>

Setting re-size boundaries

I have a horizontally re-sizable side-bar which itself is composed of two sections which are vertically re-sizable (here's how my-JSFiddle)
I achieved this by adding divs which act as draggable re-sizing handlers and then I can act on it's mouse events ..
$(document).mousemove(function(e){
$('#content').css("width",e.pageX+2);
('#history').css("left",e.pageX+2);
})
Now I wish to apply the following constraints:
min width on the side bar, such that I cannot completely collapse the side-bar
min and max height on the two sections of the side bar.
These constraints are similar to those in JSFiddle itself ..
Will I have to do this via JavaScript only or is there a way to achieve this via CSS.
i added a new FIDDLE for you
in your case you have to check the current width and select the min width or height from css
like this
....
var cvmw = parseInt($('#content').css('min-width'));
....
and than check if its reached
$(document).mousemove(function (e) {
// get current with
var w = parseInt($('#content').css("width"));
// check if with bigger than min-with
if (w > cvmw) {
$('#content').css("width", e.pageX + 2);
$('#history').css("left", e.pageX + 2);
} else {
$('#content').css("width", cvmw + 1);
$('#history').css("left", cvmw + 1);
$(document).unbind('mousemove');
}
})
here the changed css, there are some more changes made for each section
#content {
background-color: WhiteSmoke;
width:75%;
height: 80%;
min-width: 100px; /* added this min-width for further actions */
float: left;
position: absolute;
top:30px;
bottom: 100px;
}
guess you can do some other actions to keep the mouse bind to drag when hovering something like the dragline

Categories

Resources