jQuery Blind Effect on Dragged Divs - javascript

I've got a number of divs on a page that I can drag and drop around. I also implemented the blind effect on them so that I can minimize and maximize the content if i don't want to see it.
I've got a problem that if I have 3 items stacked on top of each other, vertically, and I move the bottom one to the right of the top one and minimize the top div, everything slides upwards - and the 3rd div that I moved up slides right off the screen!
I've tried a ton of stuff, like making divs use absolute positioning but that causes problems of divs not sliding upwards in some circumstances. Reordering the divs dynamically causes the divs to be thrown around the screen because of offsets and relative positioning.
I just want it so that when the user drags divs over to the left or right and an "earlier" div is minimized, all subsequent divs don't get moved.
Any suggestions on this one are greatly appreciated.
Edit 1:
The problem I'm having with the absolute positioning is as follows.
I start with A, B, and C in a vertical column. All items are expanded. I move B to the right side of A and C right under B. This gives me 2 columns (A being one and B,C being the other). With everything being absolute, if I try to close B, then C doesn't move up - rightfully so.
I tried then making things "selectively" absolute, thereby flipping between relative and absolute but I got into a problem with coordinates. If you have a relative position and left:100px and top:-50px, then flipping the position to absolute causes these coordinates to be interpreted within an absolute context. My control flies off the screen. I tried fixing this by getting the absolute coordinates using jQuery's offset function, however this returns the relative coordinates and I'm stuck. I tried to maintain the absolute coordinates myself, but it didn't work either for some reason. It's getting out of control :).
Javascript
This javascript bind is called when the page is loaded. I bind this function to a PNG arrow so that when the arrow is pressed, the content in the appropriate div expands and contracts.
$('.ArrowMargin').bind('click', function () {
var splits = this.src.split("/");
var action = "";
if (splits.length >= 2) {
var folder = splits[splits.length - 2];
var image = splits[splits.length - 1];
if (folder == "Images") {
if (image == "arrow_open.png") {
action = "close";
this.src = "Images/arrow_closed.png";
} else {
action = "open";
this.src = "Images/arrow_open.png";
}
}
}
var divs = document.getElementsByTagName("div");
if (action != "") {
var options = {};
for (var i = 0; i < divs.length; i++) {
var element = divs[i];
if (element.className.indexOf("Hideable") != -1) {
if (this.parentNode.parentNode == element.parentNode) {
if (action == "open") {
var jQueryObj = jQuery(element);
jQueryObj.show("blind", options, 500, null);
} else {
var jQueryObj = jQuery(element);
jQueryObj.hide("blind", options, 500, null);
}
break;
}
}
}
CSS
This is the CSS stuff where I set up some simple styles. I have some empty styles that I use to access divs based on class.
.ArrowMargin { float:right; margin:0 5px 0 0; }
.alpha { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.bravo { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.delta { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px;}
.charlie{}
.echo{}
HTML BODY
This is the body. It's just a number of divs that represent different pieces of content. The divs marked with the "Hideable" class are those that are jQuery blinded.
<body>
<div class="alpha">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar1</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable Center"></div>
</div>
<div class="bravo">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar2</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable charlie"></div>
</div>
<div class="delta">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar3</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable echo"></div>
</div>
</body>

Related

find number of child divs which can fit into parent divs

I have the following scenario:
I have an API which returns multiple div's. And in my UI, I have one parent div in which i need to show these div's. The condition is, if child div is overflowing parent, I need to show them on next page.
For eg: lets say my API is returning string like this:
<div class="ab0"></div>
<div class="ab1"></div>
<div class="ab2"></div>
<div class="ab3"></div>
<div class="ab4"></div>
and the parent div can fit in only ab0, ab1, ab2. Then I want to show these 3 div's 1st and when user click on '>' symbol I need to show ab3, ab4. Also if ab2 is partially overflowing and if I can show only overflowing part on next page, that will be great.
Is there any way I can do this.
Thanks in advance
A simple suggestion is to use the system's scroll functionality.
The system 'knows' how much it can show at once and as long as you can find out the height of the parent div you can move up and down the children (or part children if an exact number don't fit into the parent at once) using Javascript scrollTop.
const parent = document.querySelector('.parent');
const h = parent.offsetHeight;
let page = 0;
const lastPage = Math.floor((parent.scrollHeight + 1) / h);
function next() {
if (page < lastPage) {
page++;
parent.scrollTop = parent.scrollTop + h;
}
}
function prev() {
if (page > 0) {
page--;
parent.scrollTop = parent.scrollTop - h;
}
}
.parent {
height: 64px;
overflow: auto;
}
.parent div {
height: 2em;
border: 1px solid;
position: relative;
}
button {
font-size: 2em;
}
<div class="parent">
<div class="ab0">0</div>
<div class="ab1">1</div>
<div class="ab2">2</div>
<div class="ab3">3</div>
<div class="ab4">4</div>
</div>
<button onclick="prev();"><</button>
<button onclick="next();">></button>
</button>

Only allow scrolling to stop on certain elements

I've tried searching to see if there's already a question about this but can't find anything - so apologies if this is in fact a duplicate!
I've seen on some websites a feature where, when scrolling, the scroll stop point is forced to stop at a specific element rather than just wherever the user actually stopped scrolling.
I imagine this can be achieved via jQuery, but can't seem to find any documentation or help articles about it.
So, here's some example HTML...
<div id="one" class="block"></div>
<div id="two" class="block"></div>
<div id="three" class="block"></div>
With this as the CSS...
#one {
background: red;
}
#two {
background: green;
}
#three {
background: yellow;
}
.block {
width: 200px;
height: 100vh;
}
And what I'm looking to achieve is that when the user scrolls their browser from div 'one' to div 'two', once they've started scrolling over div 'two' and they then stop scrolling the browser automatically jumps them so that they see div 'two' in full, rather than a bit of the bottom of div 'one' and then most of div 'two' - I've definitely seen it done before but no idea how!
I hope this makes sense, and thanks in advance for any help or insight anyone can offer...
I don't remember too well, but I guess there are many ways to achieve what you want. One thing that came to my mind is to wrap around your divs and make a separate hidden div with full height. I did this adhoc solution below:
Once scroll approaches a threshold, I move to the div I should be looking at and vice versa. Here is a working solution FIDDLE:
HTML
<div id="phantom"></div>
<div id="wrapper">
<div id="one" class="block"></div>
<div id="two" class="block"></div>
<div id="three" class="block"></div>
</div>
CSS
#one {
background: red;
}
#two {
background: green;
}
#three {
background: yellow;
}
.block {
width: 200px;
height: 100vh;
}
#wrapper {
overflow:hidden;
position:fixed;
top:0px;
}
#phantom {
visibility:hidden;
}
JS
!function(){
//the array of divs
var divs = Array.prototype.slice.call(document.getElementsByClassName("block")), count = divs.length,
wrapper = document.getElementById("wrapper"),
phantom = document.getElementById("phantom"),
//the speed of scroll
scrollStep = 5,
//total length of phantom div
totalLength = Array.prototype.slice.call(wrapper.children).reduce(function(ac,d,i,a){return ac += d.clientHeight},0),
//store the animation frame here
currentFrame;
//wrapper is overflow hidden
wrapper.style.height = totalLength/count + "px";
//phantom has full height
phantom.style.height = totalLength + "px";
//add listener for scroll
window.addEventListener("scroll",function(){
//throttle the function
if(this._busy){return};
this._busy = true;
var that = this;
window.requestAnimationFrame(function(){
that._busy = false;
var heightOfDocument = Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),
totalScroll = Math.max(document.body.scrollTop,document.documentElement.scrollTop),
//which element should we look at?
whichElement = Math.round(totalScroll/heightOfDocument*count);
//if we are already around, don't do anything
if(divs[whichElement]._current){
return;
} else {
//cancel the last animation if any and start a new one
window.cancelAnimationFrame(currentFrame);
divs.forEach(function(d,i){delete d._current});
moveTo(divs[whichElement]);
}
});
},false);
//helper function to linearly move to elements
function moveTo(node){
node._current = true;
var top = node.offsetTop,
current = node.parentNode.scrollTop,
distance = top - current,
step = distance < 0 ? -scrollStep : scrollStep;
if(Math.abs(distance) < scrollStep){
node.parentNode.scrollTop = top;
return;
} else {
node.parentNode.scrollTop += step;
}
//store the current frame
currentFrame = window.requestAnimationFrame(function(){
moveTo(node);
});
}
}();
You obviously need to attach 'resize' event to update the values of the totalLength and set the correct new length on wrapper and phantom. You can also implement a easing function to modify the scrolling behavior to your taste. I leave them to you as homework.

How to can I have a sliding bar that slides on click in my nav bar using JQuery

Currently, I have three tabs in my nav bar, BOARD, SKILLS, and ABOUT, all in one container with a boarder-bottom for the container. When I click on one of the divs, the appropriate div name is selected, to indicate which tab I am on. That is what is currently working and can be seen in my codePen.io:
What I have so far - click here
.
What I am trying to do is when I go from BOARD to SKILLS or BOARD to ABOUT, is to have a bar slide from one one tab to the next, rather than being static-like (which is what I have currently) e.g. a smooth scroller on click from one tab to the next. How can I go about doing this? I have no idea where to begin.
You can use this function to slide an element:
function scaleSlider(to) {
var $slider = $('.slider', '.tabs'),
$elSpan = to.find('span'),
width = $elSpan.width(),
left = $elSpan.position().left;
$slider.animate({
width: width,
left: left
});
}
In your HTML you need to add the .slider element:
<div class="col-md-8 tabs">
<div class="slider"></div>
<!-- your html here -->
</div>
CSS:
.tabs .slider {
position: absolute;
height:100%;
border-bottom: 4px solid grey;
}
So when you click a menu element you call scaleSlider:
$('.skills').on("click", function() {
//Your code here
scaleSlider($(this));
});
Please check out this demo: http://codepen.io/anon/pen/EyoBmg

Javascript: prevent internal element "scrolling" in an element

I have a script that has a div with a width larger than its' parent, with the parent being set to overflow: hidden;. I have javascript that is setting the left positioning of the big div to create "pages". You can click a link to move between pages.
All of that works great, but the problem is if you tab from one "page" element to another, it completely messes up all the left positioning to move between the pages.
You can recreate this bug in the fiddle I set up by setting your focus to one of the input boxes on page ONE and tabbing until it takes you to page two.
I've set up a demo here.
The code that is important is as follows:
HTML:
<div class="form">
<div class="pagesContainer">
<div class="page" class="active">
<h2>Page One</h2>
[... Page 1 Content here...]
</div>
<div class="page">
<h2>Page Two</h2>
[... Page Content here...]
</div>
</div>
CSS:
.form {
width: 400px;
position: relative;
overflow: hidden;
border: 1px solid #000;
float: left;
}
.pagesContainer {
position: relative; /*Width set to 10,000 px in js
}
.form .page {
width: 400px;
float: left;
}
JS:
slidePage: function(page, direction, currentPage) {
if (direction == 'next') {
var animationDirection = '-=';
if (page.index() >= this.numPages) {
return false;
}
}
else if (direction == 'previous') {
var animationDirection = '+=';
if (page.index() < 0) {
return false;
}
}
//Get page height
var height = page.height();
this.heightElement.animate({
height: height
}, 600);
//Clear active page
this.page.removeClass('active');
this.page.eq(page.index()).addClass('active');
//Locate the exact page to skip to
var slideWidth = page.outerWidth(true) * this.difference(this.currentPage.index(), page.index());
this.container.animate({
left: animationDirection + slideWidth
}, 600);
this.currentPage = page;
}
The primary problem is that whatever happens when you tab from say, an input box on page one to something on page 2, it takes you there, but css still considers you to be at left: 0px;. I've been looking all over for a solution but so far all google has revealed to me is how to stop scrollbar scrolling.
Any help or suggestions would be appreciated, thanks!
P.S. The html was set up like this so that if javascript is disabled it will still show up all on one page and still function properly.
I updated your fiddle with a fix for the first tab with the form: http://jsfiddle.net/E7u9X/1/
. Basically, what you can do is to focus on the first "tabbable" element in a tab after the last one gets blurred, like so:
$('.form input').last().blur(function(){
$('.form input').first().focus();
});
(This is just an example, the first active element could be any other element)
Elements with overflow: hidden still have scrolling, just no scroll bars. This can be useful at times and annoying at others. This is why your position left is at zero, but your view of the element has changed. Set scrollLeft to zero when you change "pages", should do the trick.

css issue: absolute positioned span would not extend out of containing div

as you can see in the following image, i have a simple form that validates the fields in real time and generates a span that says whether that field is valid or not. problem is, for some reason that span would not extend out of the containing div even though its has position:absolute in its css rule. by the way, the span gets its right position using javascript, that calculates the width of that field (since every field differs in width). any suggestions?
here is the css code:
div.row {
background:url('/img/formBackground.gif') repeat;
margin:0 2px 2px 2px;border-radius:10px 0 10px 0;
position:relative
}
.row span.valid,
.row span.invalid {
line-height:18px;
height:20px;padding:0 22px 0 5px;
display:block;font-size:13px;
border-radius:3px;position:absolute;
z-index:100;top:4px;right:160px
}
here is the html code:
<div class="row">
<label for="email">דואר אלקטרוני: <span class="required">*</span></label>
<div class="divider"></div>
<input type="text" name="email" id="email" style="width:250px" dir="ltr"/>
</div>
here is the javascript code:
email.onchange = function validateEmail() {
span = document.getElementById('span4');
span.style.right = calcDistance(email);
var reg = /^([A-Za-z0-9_\-\.])+\#([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
el = email.value;
if(el == null || el == '') {span.setAttribute('class','invalid');span.innerHTML = "<span></span>עליך להזין דוא\"ל";return false;}
if(reg.test(el) == false) {span.setAttribute('class','invalid');span.innerHTML = "<span></span>כתובת הדוא\"ל אינה תקינה";return false;}
else {span.setAttribute('class','valid');span.innerHTML = "<span></span>תקין";return true;}
}
function calcDistance(el) {
var spanDistance = 160 + 20;
var targetWidth = el.offsetWidth;
return targetWidth + spanDistance + 'px';
}
This is how I want it to look (the span extends out of the containing div). I was able to get this result only by giving a certain width to the span, which is not a good solution because every message differs in it's width:
Elements with position:relative will "trap" any absolutely positioned child elements. In other words, the absolute element's coordinates are relative to the parent.
http://css-tricks.com/absolute-positioning-inside-relative-positioning/
http://www.quirksmode.org/css/position.html (see "The containing block" section)
While calculating the input widths to create your error message width is clever, this might just be too fragile of a design. You may end up with error messages that are simply too small for the message itself, especially when you consider that you don't truly have 100% control over the user's font size.
There's not enough code here to reproduce the output in your image, but possible solutions or workarounds may include:
Remove position:relative from div.row
Change your design, and move the error message underneath the input instead
After looking into some old posts, I managed to solve the problem by adding white-space:no-wrap; to the span CSS rule.

Categories