Detecting whether overflow is coming into play on an element - javascript

I have a div, it will be a certain fixed height. Say 500px. Generally it will have content blocks longer than 500px and using overflow: auto; a scrollbar will appear in the element. However on some occasions it does not and the design looks wonky (the scroll bar is indeed a design element here).
Markup might look like this:
<div class="col2">
...
</div>
When .col2 has overflowing elements (i.e. a scrollbar) I want to do nothing, when it does not, I want to add another class (something with a border), maybe .border.
Just not sure how to go about this? Preferably with jQuery as that library is already being used. Would really appreciate any help!

This should assist you... Basically create to functions that tell you weather or not the div will have a scrollbar. (either vertical or horizontal)
$.fn.hasVerticalScrollBar = function () {
if (this[0].clientHeight < this[0].scrollHeight) {
return true
} else {
return false
}
}
$.fn.hasHorizontalScrollBar = function() {
if (this[0].clientWidth < this[0].scrollWidth) {
return true
} else {
return false
}
}
Usage
alert($('#mydivid').hasHorizontalScrollBar());
alert($('#mydivid').hasVerticalScrollBar());

Related

How can I make an element sticky relative to the window without JavaScript (If Possible)?

I've been loving position: sticky. It solves most, if not all, of the issues without resorting to JavaScript. But, I've hit a wall. I need to make an element that is nested inside a couple of <div> to be sticky. We know that position: sticky works as a blend of position: relative and position: fixed, therefore it will anchor to its first parent.
From MDN:
The element is positioned according to the normal flow of the
document, and then offset relative to its nearest scrolling ancestor
and containing block (nearest block-level ancestor)
In this case, I want to make a header sticky relative to the window and not the container. The HTML makes it difficult for me to restructure it outside nested <div>
Is this possible without JavaScript?
Here's the code:
<div class="attendance">
<!-- Here's the header I want to make sticky to the window, and not to div.attendance-->
<header class="text-center sticky">Monday 11/22/2019</header>
<!-- Header above -->
<div class="date-config">
<div class="form-group">
<input type="checkbox" id="workable" /> No Work<br />
</div>
<div class="form-group">
<label for="notes">Notes:</label>
<textarea id="notes" class="form-control"></textarea>
</div>
<label for="markall">Mark all as>
<select id="markall" class="form-control">
<option></option>
<option>Absent</option>
<option>Present</option>
</select>
</div>
<div class="student-attendance">
Hello :)
</div>
</div>
Any ideas?
P.S: I've found this, but it uses JavaScript.
Edit:
Here's an awful, but working example (Beware! It's in Spanish - Look for the dates! They won't stick to the window!).
Ok! First I'd like to apologize as this question wasn't possible to be answered without rendering the HTML. Fortunately, I have found the solution.
TL;DR In this case, no, you need JavaScript. You will need to implement a translateY transform in the element to achieve this. I don't know if the problem is that the parent element has a transform property and it causes this bug or there's something else causing the issue.
Explanation:
I'm currently using a carousel JS library called tiny slider. I'm displaying form elements instead of images, (Building a responsive table; Had issues when I tried using CSS Grids). So far, so good. The problem started when I wanted to set sticky the date headers.
I went with the modern approach of setting position:sticky, but that didn't work. The elements would get clogged in a certain position and it wouldn't move or stick. I started researching online (which ended up asking this same question), and the HTML itself. I did find that there were many parent <div>s that were created by tiny-slider. My theory was that it was getting attached to one of those parents.
Therefore, I decided to try the old tactic of combining position:fixed with a scroll event. But, that didn't work. Going back online and Google-Fuing a bit, there seems to be an old bug [1] [2] [3] that whenever a translate is applied to one of the parents an out-of-root container is created and position:fixed doesn't work as expected.
I have a hunch that this may be one of the reasons why sticky didn't work, but according to this answer, it doesn't seem like it.
I kept thinking for a while, and resorted to use a transform CSS property with translateY. I made a small experiment in the browser, and it worked!
Hence, I ended up implementing the scroll eventListener and listening to the header's parent's position, and applying getBoundingClientRect() to get the offset. If I had applied it to the element itself, it would have given me the translated position which I applied through CSS.
I was skeptical that this could be a performance bottleneck for mobile browsers. Therefore, I checked that the transform function was called inside a requestAnimationFrame and it had applied a will-change property in the CSS stylesheet.
I ran the code with a 4x CPU Slowdown in Google Chrome, and had good results 😁.
Here's the resulting function I have (Where elemsToFixed are all the <header> elements, and threshold is the top offset so it doesn't conflict with the navbar):
export function fixedHeaderScroll(elemsToFixed: HTMLHeadingElement[], threshold: number) {
if (!elemsToFixed || elemsToFixed.length === 0) {
console.error("elemsToFixed can't be null or empty");
return;
}
console.log('Total elems', elemsToFixed.length);
// We assume that all of the elements are on the same height.
const firstEl = elemsToFixed[0];
let propSet = false;
window.addEventListener('scroll', (e) => {
window.requestAnimationFrame(() => {
const top = firstEl.parentElement!.getBoundingClientRect().top;
if (top > threshold) {
if (!propSet) return;
propSet = false;
setElemsFixed(elemsToFixed, top, threshold, false);
return;
}
propSet = true;
setElemsFixed(elemsToFixed, top, threshold);
});
});
}
function setElemsFixed(elemsToFixed: HTMLHeadingElement[], top: number,
threshold: number, setFixed = true) {
console.log('SetElemsFixed is', setFixed);
elemsToFixed.forEach((elem) => {
if (!setFixed) {
elem.removeAttribute('style');
return;
}
elem.style.transform = `translateY(${(top * -1)}px)`;
});
}
The following picture shows a 4x slowdown in the CPU and the calculation of the style (With 26 elements) is about 29.4ms (Which is great!). Using Chrome 70 on Windows and i7 4700MQ processor.
As you reference to the docs, the position sticky will stick to its "nearest scrollable ancestor", which generally speaking will be any ancestor with 'display: block' (the default) and also things like 'display: flex'. You can make an ancestor not scrollable by setting 'display: contents'. (Depending on the rest of your layout and CSS this may or may not be usable.)

Dynamic view-port Calculation for sticky navbar

I'm new to JavaScript, i seem to have got myself in a tricky situation. Currently i have some code which below 200px adds a class to make the navbar sticky. The code which have added can be found below.
function navbarCollapse () {
if ($("#mainNav").offset().top > 200) {
$("#mainNav").addClass("navbar-shrink");
$("#mainNav").removeClass("navbar-font");
} else {
$("#mainNav").removeClass("navbar-shrink");
$("#mainNav").addClass("navbar-font");
}
}
//Execute The Collapsing Navbar Function
navbarCollapse();
//Scroll Event
$(window).scroll(navbarCollapse);
The problem I'm facing is 200px on one device is completely different on another device such as a large monitor
I need to be able to calculate the height of a devices view-port, and then execute the code accordingly.
Any help would greatly be appreciated. Like i said I'm new to JavaScript and I'm probably missing something simple.
Instead of making an offset like this add a class that uses css and do your calculations there.
.nav-offset {
Top: 1%;
}

Automatic extension of side borders based on page content height

I'm making a website and have "borders" around my main content. I say "borders" because its not a CSS border, but div's with a background image.
Now I have my left and right div borders (#cont-border-left/right) height set explicitly to 675px, and I have another div (#extend-l/r) just under that which I want to expand down the page when the main content goes past 675px.
I'd like to to this using only CSS if possible, but if not JavaScript/JQuery would be a great solution for me as well.
I was going to paste a bunch of the code here, but it would probably just be easier to view the source, because I think it will make more sense if you can see it all together.
Saw this on a similar question... But I'm not great with jQuery or JavaScript.
$(document).ready(function()
{
if($('#leftColumn').height() > $('#rightColumn').height())
{
$('#rightColumn').height($('#leftColumn').height());
}
else
{
$('#leftColumn').height($('#rightColumn').height());
}
});
And turn it into something like:
$(document).ready(function()
{
if($('#content').height() > $('#cont-border-left').height())
{
$('#extend-l').height = $('#content' - 645px)
^The above line needs help / correcting
}
else
{
$('#extend-l').height = 0
}
});
Any ideas on what I should try out?
EDIT2: Still would like to know if someone has a pure CSS solution!
You might be interested in CSS3 border-images. See various:
css3.info: Border-image: using images for your border
css-tricks.com: Understanding border-image
border-image-generator
border-image
Figured it out. I was close with the edit I made.
Heres the work code should anyone need something similar in the future.
$(document).ready(function()
{
if($('#content').height() > $('#cont-border-left').height())
{
$('#extend-l').height($('#content').height()-675)
}
if($('#content').height() > $('#cont-border-right').height())
{
$('#extend-r').height($('#content').height()-675)
}
});

Scrollable div without overflow:auto?

In my app I have 2 divs, one with a long list of products that can be dragged into another div (shopping cart). The product div has the overflow but it breaks prototype draggable elements. The prototype hacks are very obtrusive and not compatible with all browsers.
So I am taking a different approach, is it possible to have a scrollable div without using CSS overflow:auto?
Theres a css property to control that.
<div style="width:100px;height:100px;overflow:scroll">
</div>
http://www.w3schools.com/Css/pr_pos_overflow.asp
You can use a frame with content larger than its window. Might make it hard to pass JS events though.
Here is what I wrote to have it running under IE 8.0.6 & Firefox 3.6.3:
Make draggable the elements (with border) in the "width:100px;scrollable:auto" container:
function makeDraggable(container,tag) {
if(!container || !tag) { return false; }
$(container).select(tag).each( function(o) {
new Draggable(o,{
starteffect: function(e){makeDragVisible(container,e);},
endeffect: function(e){e.setStyle({'position':'','width':'','cursor':''});},
zindex: 1000
// , revert: ... // the other options
});
});
}
function makeDragVisible(container,element) {
if(!container || !element) { return false; }
var i=$(container).getStyle('width');
i=i.replace('px','');
i=Math.round(i-20)+'px';
element.setStyle({'width':i,'z-index':1000,'position':'absolute','cursor':'move'});
//
$(container).setStyle({});
}
Important notes:
the z-index is repeated
notice the container loss of style at the end of 'starteffect'. Cursor and width are simply there to keep the drag user friendly.
I hope it helps.

jCarousel with unknown content width

I am trying to use jCarousel plugin for jQuery in order to provide my website users with scrollable (horizontal) content.
The content I am mentioning is basically user defined <li> elements, styled so that they have a feel and look of a tab. so basically I am trying to achieve the same effect of the tabs in pageflakes.com. As you may imagine, the users are creating tabs and providing tab names by themselves..
jCarousel needs you to specify a fixed width for the content e.g., all their examples are based upon images that have fixed height and width. but in my case I have not control over what the user will name his/her tab...making it impossible for me to guess the width of the total container div.
I have tried using a silly method such as guessing the width programatically assuming each letter being approx 5 pixels and multiplying 5 with the length of the word they have given as a name for a tab. even in this case, i needed to manipulate the css file dynamically which I am not sure how to do and even if it is feasable to do so..
Any solutions appreciated...
<lu>
<li class='MyTab' id='578'>This is my tab</li>
<li class='MyTab' id='579'>of which I can</li>
<li class='MyTab' id='580'>never guess</li>
<li class='MyTab' id='581'><img src='img/bullet_key.png' /> The length of</li>
</lu>
The above html is produced programatically through ajax_tabs_output.aspx, loaded into a javascript array and the jCarousel takes care of the rest..
function outputTabsArray() {
$.ajax({
url: 'ajax_tabs_output.aspx',
type: 'get',
data: 'q=array',
async: false,
success: function(out)
{
tabs_array = out;
}
});
}// end outputTabsArray
function mycarousel_itemLoadCallback(carousel, state)
{
for (var i = carousel.first; i <= carousel.last; i++) {
if (carousel.has(i)) {
continue;
}
if (i > tabs_array.length) {
break;
}
carousel.add(i, mycarousel_getItemHTML(tabs_array[i-1]));
}
};
/**
* Item html creation helper.
*/
function mycarousel_getItemHTML(item)
{
return '<div class="MyTab" id="' + item.tab_id + "'>" + item.tab_name + '</div>';
};
$('#mycarousel').jcarousel({
size: tabs_array.length,
itemLoadCallback: {onBeforeAnimation: mycarousel_itemLoadCallback}
});
The closest thing to what you want is probably jscrollhorizontalpane. I've never used it so I can't vouch for it's effectiveness as a solution to this specific problem.
This sort of widget shouldn't be to hard to make if you want to attempt it. I'll break down the approximate method I would use:
Make sure to use plenty of wrappers.
<div class="scrollable">
<div class="window">
<div class="space">
<ul class="tabs">
<li class="tab">...</li>
<li class="tab">...</li>
<li class="tab">...</li>
</ul>
</div>
</div>
←
→
</div>
What we'll be doing is shifting the "space" element back and forth inside "window" element. This can be done by setting position:relative to "window" and position:absolute to "space" and then shift it about using left:-??px, but I'll use the scrollLeft property.
Add some CSS.
.window {
overflow : hidden;
width : 100%;
}
.space {
width : 999em; /* lots of space for tabs */
overflow : hidden; /* clear floats */
}
.tabs {
float : left; /* shrink so we can determine tabs width */
}
.tab {
float : left; /* line tabs up */
}
This is just the basic stuff that the technique needs. Some of this stuff could be applied by jQuery,
Add events to window resize.
$(window)
.resize(function () {
var sz = $('.window');
var ul = sz.find('ul');
if ( sz.width() < ul.width() ) {
$('.scrollable a.left, .scrollable a.right').show();
}
else {
$('.scrollable a.left, .scrollable a.right').hide();
}
})
.trigger('resize');
Add events to scroll buttons.
$('.scrollable a.left').hover(
function (e) {
var sz = $('.window');
sz.animate({ scrollLeft : 0 }, 1000, 'linear');
},
function (e) {
$('.window').stop();
});
$('.scrollable a.right').hover(
function (e) {
var sz = $('.window');
var margin = sz.find('ul').width() - sz.width();
sz.animate({ scrollLeft : margin }, 1000, 'linear');
},
function (e) {
$('.window').stop();
});
Done!
The widths could also be calculated by looping through the "tab" elements and summing upp outerWidth. This is unnecessary if you have full control but might be a better choice for a full standalone plugin.
From what I can tell, you're trying make JCarousel do something it was never designed to do. Based on what I read on the JCarousel website it appears to be an image rotator.
What it sounds like you want is a tabbed interface. See JQuery UI Tabs for a demo and documentation on how to implement it.
If I'm totally wrong and all you're looking for is a tutorial on how to do proper CSS tabs, have a look at:
http://unraveled.com/projects/css_tabs/
Soviut,
You are actually quite right! I am trying to make JCarousel do something it wasn't designed for.
However, I also wouldn't like to use a tab plugin or any similar stuf as I NEED FULL CONTROL over my output. Such as injecting more elements into the tabs when needed such as double clicking, loading and many other etc. etc.
Actually what I am looking for a way to scroll horizontally the content within a div with arrows on the left and right.. To be more precise, I need the exact same structure of the tabs seen in www.pageflakes.com
The user will be able to create tabs by clicking on a link (or any other element for that matter), they will be able to inline edit its name, whenever they have more tabs then the length of the div, the buttons will be visible allowing them to slide inside the div, I will have events bound to their load, click and double click events.
To summarize I have everything ready and working except for the sliding/scrolling part. Its only I have to get help from you guys regarding how to achieve this functionality..
Kind regards,
What you're looking for isn't tabs, but draggable panels. One example can be found here, called JQuery Portlets. The related blog entry about Portlets can be found here.
You may also want to look into JQuery UI's Draggable, Droppable, and Sortable plugins.

Categories