Making a calendar component scrollable on x and y axis - html

I'm creating a calendar component with my own HTML/CSS.
It can have multiple categories across it's header horizontally (x-axis) and it will have up to 24 time slots down it's side vertically (y-axis).
The category headers must always be visible when scrolling vertically and the time slots must always be visible when scrolling horizontially.
How can I achieve this via css?
See screenshot for what I want (Image more tracks across header that results in content overflow).
Maybe take this as my sinple html structure:
<div class="calendar">
<div class="calendarColumnHeaders"></div>
<div class="calendarGrid">
<div class="timeSlotsColumn"></div>
<div class="tracksContainer"></div>
</div>
</div>
Thanks

Give <div class="tracksContainer"></div> an extra class called stick
add this Css:
.stick {
position:fixed;
top:0px;
}
And unfortunately, you do need some Jquery
jQuery – Calculates the position of the sticker div and makes its position fixed if the page has scrolled that far.
$(document).ready(function() {
var s = $(".tracksContainer");
var pos = s.position();
$(window).scroll(function() {
var windowpos = $(window).scrollTop();
s.html("Distance from top:" + pos.top + "<br />Scroll position: " + windowpos);
if (windowpos >= pos.top) {
s.addClass("stick");
} else {
s.removeClass("stick");
}
});
});

overflow-x: scroll; //horizontal
overflow-y: scroll; //vertical

Related

Set all panels width to the width of the widest panel

I have jquery tabs and i would like to make all panels width be the same as the widest panel width.
Something like heightStyle:"auto" works for height.
widthStyle:"auto" would be what im looking for, but it doesn't exist.
Comparison of how it looks vs how i would like it to look:
<div id="tabs" class="tab" >
<ul>
<li><span>One</span></li>
<li><span>Two</span></li>
<li><span>Three</span></li>
</ul>
<div id="f1">aaaaaaaaaaaaaaaaaaaaaaaaaa</div>
<div id="f2">bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</div>
<div id="f3">c<br>c<br>c<br>c<br>c<br>c<br></div>
</div>
<div style="clear:both">
</div>
<div id="tabs2" class="tab" >
<ul>
<li><span>One</span></li>
<li><span>Two</span></li>
<li><span>Three</span></li>
</ul>
<div id="f1">aaaaaaaaaaaaaaaaaaaaaaaaaa</div>
<div id="f2">bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</div>
<div id="f3">c<br>c<br>c<br>c<br>c<br>c<br></div>
</div>
<script>
$("#tabs").tabs({
heightStyle: "auto",
}).css("float", "left");
$("#tabs2").tabs({
heightStyle: "auto",
}).css("float", "left").width(485);
</script>
http://jsfiddle.net/knj73r9o/2/
I'd like to do it without hardcoding the width but automatically using the contents width. Is something like that possible?
To address this, you will want to iterate each panel and determine the largest width value. Since the width is collapsed, you have to also activate each panel. Something like this will help:
Example: https://jsfiddle.net/Twisty/knj73r9o/24/
JavaScript
$(function() {
function findMaxWidth($tab) {
var mw = 0;
var oInd = $tab.tabs("option", "active");
$tab.find(".ui-tabs-panel").each(function(ind, el) {
$tab.tabs("option", "activate", ind);
console.log("Activate Tab " + ind);
if ($(el).width() > mw) {
mw = $(this).width();
console.log("Tab " + ind + " Width: " + mw + "px");
}
});
$tab.tabs("option", "active", oInd);
return mw;
}
$("#tabs").tabs({
heightStyle: "auto",
}).css("float", "left");
var widthStyle = findMaxWidth($("#tabs"));
$("#tabs div").each(function() {
$(this).css("width", widthStyle + "px");
});
$("#tabs2").tabs({
heightStyle: "auto",
}).css("float", "left").width(485);
});
Once you have the width, you can use .css() to set the width of each.
If you need to do this a lot, or on many items, consider using $.widget() to set your own widthStyle option or function.
Hope it helps.
You can use CSS and have the tabs expand to width of 100%. This would expand to the width of the parent container.
.tab{
width: 100%;
}

Bootsrap Affix not working

I have added the library for Bootstrap and data-spy attribute where I want to make the div fix when I scroll the page down. But it doesn't work, I have almost tried everything, but not able to figure out the problem.
Is is something like the data-spy attribute doesn't work on class = "row" ?
Here's my code for HTML.
<div class="row">
<h4> HEADING </h4>
<h5>
<div class="row" data-spy="affix" data-offset-top="10">
dsds
Date : <input type="date" name="graph_date" id="graph_date">
</div>
<div class="row">
<div class="graph-hourly">
<div class="loader" id="chart_loader">
<p>Loading...</p>
</div>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_hourly"></div>
</div>
</div>
</div>
and some css :
.affix {
top : 0;
width: 80%;
}
after searching for some solutions, I've added this also,
.affix-top {
width: 100%;
}
.affix-bottom {
position: absolute;
width: 100%;
}
but this solution also dosen't worked for me.
Not sure what the problem is in your case. I copied and pasted your code into a jsfiddle with the bootstrap library and the affix class did work. Though, it worked badly because it affixed the row right when you started scrolling.
Looks like Bootstrap doesn't have a way to set the offset to the current position of the element so I added the following javascript to make it work.
$('#affix-this').affix({
offset: {
top: $('#affix-this').offset().top
}
})
(#affix-this should be changed to the id of the row you want to affix.)
Note the $('#affix-this').offset().top. This makes sure the element gets affixed right when you reach the element's current position.
Second, I removed the html attributes that you had for the affixing.
<div class="row">
<h4> HEADING </h4>
<div class="row" id="affix-this">
dsds Date :
<input type="date" name="graph_date" id="graph_date">
</div>
<div class="row">
<div class="graph-hourly">
<div class="loader" id="chart_loader">
<p>Loading...</p>
</div>
<div id="chart_hourly"></div>
</div>
</div>
Notice the affix-this id was added to the row that you want to affix.
Here is a working JSFiddle with these changes so you can see it in action: https://jsfiddle.net/heraldo/6s4u26m3/4/
First of all delete top:0; from affix class because it will make a issue for you.
now you have two methods pick a one :
1
adding data-spy="affix" which is works fine for me
2
same result ass data-spy but you will need some styling after you complete your page
by adding a position Property for input tag
as ex. :
CSS:
sticky{
position:fixed;
}
and HTML :
<input type="date" name="graph_date" class="sticky" id="graph_date">
Update 1
this Jquery code can detect a scroll event so when user scroll down it will make the div tag sticky or "affix"
$(function() {
$(window).scroll(function() {
var aTop = "100";
if ($(this).scrollTop() >= aTop) {
$('#affix-this').css( "position", "fixed" );
$('#affix-this').css( "top", "0" );
$('#affix-this').css( "width", "100%" );
}
});
});
change the aTop variable with the height you want (in pixel) so when the user scroll down 100px the div become sticky
a JSfiddle example
Update 1.1
a bit smarter Jquery code do the same but get the height automatically from a another element this can be good if you format your page to something similar to this
$(function() {
$(window).scroll(function() {
var aTop = $('id').height();
if ($(this).scrollTop() >= aTop) {
$('#affix-this').css( "position", "fixed" );
$('#affix-this').css( "top", "0" );
$('#affix-this').css( "width", "100%" );
}
});
});
Make sure the element to which you're adding data-spy="affix has been created in the DOM before your Bootstrap scripts load. I ran into an issue where I was adding data-spy="affix" in my HTML, but it was wrapped up in a section that wasn't rendering, thanks to data-ng-if. My HTML was created after my Bootstrap had loaded, so the <div> I wanted to stick to the top of the screen never stayed in a fixed position. If you can, use data-ng-show, or something that merely hides HTML, rather than prevents it from being created on page load.

CSS transition of height for injected content into a DIV

I'm having a following template:
<div class='content'>
{{content}}
</div>
And following style:
.content {
margin-top: 30px;
background-color: skyblue;
width: 300px;
transition: all linear 0.5s;
}
Please note that {{content}} will grow or shrink (technically to extend a card to show more information or hide it). I've already set the css transition and it does work when manually setting a different height to the element. However when more content is injected into content no transition is made, just a plain old resizing. Any help of getting the transision right?
See following plunkr
Thanks,
Amit.
I believe that's quite normal, transitions apply only to changes to the CSS, not for computed changes.
One option might be to have a nested div, set overflow: hidden on the outer one, then get the computed height of the inner one and set it on the outer one to get the transition.
CSS:
#outer {
margin-top: 30px;
background-color: skyblue;
width: 300px;
transition: all linear 0.5s;
overflow: hidden;
}
HTML:
<button id="more" onclick="increase();">More</button>
<button id="more" onclick="decrease();">Less</button>
<div id="outer">
<div id="inner">
lorem ipsum
</div>
</div>
JS:
function increase()
{
var o = document.getElementById('inner');
var t = o.innerHTML;
for (var i=0 ; i<20;i++)
t += " lorem ipsum";
o.innerHTML = t;
adjustHeight();
}
function decrease()
{
var o = document.getElementById('inner');
o.innerHTML = "lorem ipsum";
adjustHeight();
}
function adjustHeight()
{
var i = document.getElementById('inner');
var o = document.getElementById('outer');
o.style.height = window.getComputedStyle(i).height;
}
Working fiddle: http://jsfiddle.net/Lnts7w1f/
With the help of #jcaron answer I managed to sort this in a react component. So when the 'activeSubsection' button is clicked the useEffect hook is run.
const [topicsHeight, setTopicsHeight] = React.useState("0px")
const topicsDiv = React.useRef();
React.useEffect(() => {
setTopicsHeight(topicsDiv?.current?.offsetHeight + "px")
}, [activeSubsection])
{!hideTopics &&
<div style={{ overflow: "hidden", height: topicsHeight, transition: "0.2s" }}>
<Topics ref={topicsDiv} className="largeScreen">
{!!subsectionTopics && subsectionTopics.sort((a, b) => a.node.slug.localeCompare(b.node.slug)).map((topic, index) =>
<Link key={index} href={'/'+slug+'/'+topic.node.slug} scroll={false}>
<Topic
active={activeTopic == topic.node.slug} transition={true} bold={activeTopic == topic.node.slug}
>
{topic.node.title}
</Topic>
</Link>
)}
</Topics>
</div>
}
Ovbiously that's not the full component but hopefully enough to give you an idea.
Well... There's a trick you can do for that... I don't know if it will fit your needs.
The css transition effect is applied on css properties that have a previous value, and then change. Though you are indeed changing the content's height of the div, the actual css property height is not explicitly changing. That's why you don't get the animation.
A workaround is to find the inner height of the div and then set it to the element, causing it to animate. I've created a function for that, and added a span to control the inner size of the div.
Just call the function on every change:
<div class='content'>
<span id="height-control">
{{ctrl.content}}
</span>
</div>
JS function:
var spn = document.getElementById("height-control");
var UpdateHeight = function () {
var h = spn.offsetHeight;
spn.parentNode.style.height = h + "px";
};
http://plnkr.co/edit/p6QRAR5j4C8d0d0XRJRp?p=preview

Expanding Vertical Space Issue with Floated Elements

I have a layout that requires a list of items to be organized into two vertical columns. The items are in a single list of <div>s, with the markup also being re-purposed for mobile, so I don't want to modify the structure.
The problem I have here is that each item has an expanding content area which the user can toggle, and when this content is expanded the vertical space for that column needs to expand downward with the other column staying fixed.
Right now I have a basic solution with floated items, but when I expand the content areas the vertical space expands in both columns instead of just the one.
Here's a link to an example of the functionality as I have it now, and below is a screenshot of what the desired behavior should be.
Is it possible to style this to support the required behavior? Or am I going to have to modify the structure of items in order to get this to work? Thanks in advance for your help!
Your premise is flawed. Document structure flows left-to-right, top-to-bottom. You will need to make some change to the structure... Easiest would be adding two containers for a left column and a right column. Otherwise, you're in for some tricky absolute positioning markup, and a little funky jquery, which I can only suggest with the addition of some unique IDs for each of the panels.
I would, personally, add ids such as panel1 through panel4 per your example, then use this javascript (or similar) as a jumping off point:
for(var i=1; i<=4; i++) {
$('#panel'+i).css('left', function(index) {
if(i%2 == 0) return "120px";
else return "0px";
});
}
$('.more').click(function(e) {
e.preventDefault();
$(this).parent().children('p').toggle();
var id = $(this).parent().attr("id");
switch( id ) {
case 'panel1':
console.log("panel1 found");
$('#panel3').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
case 'panel2':
$('#panel4').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
default: break;
}
});
With the default values in the css for those panels:
#panel1 { top:0px; }
#panel2 { top:0px; }
#panel3 { top:56px; }
#panel4 { top:56px; }
The less you tweak the html, the more work you'll create in javascript.
edit:
Proposed alternate Javascript to remove need to alter HTML, assuming two elements per row. Since we know the row to be the problem...
var ct = 1
$('#container > div').each(function(index, domEle) {
$(domEle).attr('id', 'panel'+ct);
$('#panel'+ct).css({
'position': 'absolute',
'left' : function(index, value) {
if(ct%2 == 0) return "120px";
else return "0px";
},
'top' : function(index, value) {
return (56 * Math.floor(ct/3)) + "px";
}
});
ct++;
});
$('.more').click(function(e) {
e.preventDefault();
$(this).parent().children('p').toggle();
var id = $(this).parent().attr("id");
switch( id ) {
case 'panel1':
$('#panel3').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
case 'panel2':
$('#panel4').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
default: break;
}
});
Now no changes need be made to the HTML, though you'll want to redo the click function to handle repositioning of elements after a click. I would make life easy and hide all .more items before expanding a new box, since it would mean having to calculate the heights of all elements above, but how much work you want to make is your business.
Here's a PURE CSS SOLUTION for four panels (I don't know if you intended to have more, and I do not have six [2 wide 3 high] working yet--and suspect it is not possible). It works in FF and IE8/9. See the fiddle: http://jsfiddle.net/bEgwB/203/
However, IE8 experiences a redraw bug that keeps panel 3 from moving so it needs extra javascript help (added in fiddle above), and IE7 needs some margin adjustments to get panel 4 positioned correctly (but does not have the redraw issue even without the extra javascript help). UPDATE (11-18-11): here's the fiddle for IE7 margin adjustment: http://jsfiddle.net/bEgwB/286/
EDIT: a previous version of my CSS had display calls that were unnecessary.
HTML
<div id="container">
<div class="panel one">
Panel 1<br />
<a class="more" href="#">more</a>
<p>More Info 1 with some additional content</p>
</div>
<div class="panel two">
Panel 2<br />
<a class="more" href="#">more</a>
<p>More Info 2 with some additional content</p>
</div>
<div class="panel three">
Panel 3<br />
<a class="more" href="#">more</a>
<p>More Info 3 with some additional content</p>
</div>
<div class="panel four">
Panel 4<br />
<a class="more" href="#">more</a>
<p>More Info 4 with some additional content</p>
</div>
</div>
CSS
a {color:yellow;}
#container {width:0 ; padding: 0 130px;}
.panel {
background-color:green;
padding:5px;
color:#fff;
width:100px;
}
.panel p {display:none;}
.panel.one {
float: left;
margin:10px 0 10px -120px;
}
.panel.two {
float: right;
margin: 10px -120px 20px 0;
}
.panel.three {
float: left;
clear: left;
margin:10px 10px 10px -120px;
}
.panel.four {
clear: right;
margin: 10px;
}
JAVASCRIPT
$('.more').click(function(e) {
e.preventDefault();
$(this).parent().children('p').toggle();
/* the following fix IE8 */
$(this).parent().parent().hide();
$(this).parent().parent().show();
});
You need to add two more div's - left and right column and split your items between these two divs. This is the way how to make them independent, here is jsfiddle for this.
HTML
<div id="container">
<div class="left">
<div class="panel">
Panel 1<br />
<a class="more" href="#">more</a>
<p>More Info 1 with some additional content</p>
</div>
<div class="panel alt">
Panel 2<br />
<a class="more" href="#">more</a>
<p>More Info 2 with some additional content</p>
</div>
</div>
<div class="right">
<div class="panel">
Panel 3<br />
<a class="more" href="#">more</a>
<p>More Info 3 with some additional content</p>
</div>
<div class="panel alt">
Panel 4<br />
<a class="more" href="#">more</a>
<p>More Info 4 with some additional content</p>
</div>
</div>
</div>
CSS
a {color:yellow;}
#container {width:300px; position:relative;}
.panel {background-color:green;padding:5px;color:#fff;width:100px;margin:10px;}
.panel p {display:none;}
.left {
width: 50%;
float: left;
}
.right {
width: 50%;
float: right;
}
May be you can do it column-count property like this:
a {color:yellow;}
#container {
width:300px;
-moz-column-count: 2;
-moz-column-gap: 50%;
-webkit-column-count: 2;
-webkit-column-gap: 50%;
column-count: 2;
column-gap: 50%;
}
.panel p {display:none;}
.panel {background-color:green;padding:5px;color:#fff;width:100px;margin:10px;}
.alt{
margin-bottom:90px;
}
Check this fiddle http://jsfiddle.net/bEgwB/87/
UPDATED
Check this:
http://jsfiddle.net/bEgwB/276/
There is a styling solution. To tie the two columns together they need an extra level of binding you can do that by adding a style attribute to "container", "display:table" and to the panels with "display:table-cell".
That will keep the heights in synch. Both "container" and the "panel" class must have a declared width or they really mess up the layout. IE is weak on support of these attributes, so that could be a problem with the solution.
If I correctly inderstand, that do you you want is something like this
http://jsfiddle.net/bEgwB/275/
You need to use both css properties display:inline-block and float:left to implement effect like on your jpg. If my markup right and this looks as expected, I may help you with javascript, if it not ok now
Good luck.

How to freeze header of the page

My web template is based on div tags. I want to freeze header part (Header, logo etc), I mean when you scroll the page the header should be in fixed position.
<!--Header and Logo -->
<div id="outer">
<div id="header">
<h1>Some Application</h1>
<img src="/media/images/logo.gif" height="95" width="190">
</div>
Here is my css
#outer
{
}
/* Header */
#header
{
height: 95px;
background-image: url();
background-repeat:no-repeat;
background-position: bottom left;
padding-left: 20px;
padding-top: 15px;
padding-bottom: 15px;
}
Can some one help me? Thanks
Try using the position: fixed; property. Here is an example fiddle:
http://jsfiddle.net/austinbv/2KTFG/
You want position: fixed.
A nifty CSS attribute:
#outer {
position: fixed;
}
Freeze header at top (with correct header widths and alignment)
position:fixed; makes the header sit still but is far short of all the behavior I'd expect from a header - stick at the top of the screen when the actual table headers are scrolled up off the screen, and keep the widths and html of the frozen header synced with the actual table. This solution...
declares a function resizeHdr() that will iterate through the tblData first row <th>'s and grab existing width and HTML
sets global variables for the window and placeholder for how far down the screen the tblData top is
on $ready, inserts a new, hidden shell table that at the top of the <body>
sets the placeholder
grabs width, margins from tblData
calls resizeHdr() to populate the hidden cloned table
sets a watcher for scrolling: y-scrolling shows the cloned header when the top of tblData (main table) passes the screen top and hides it again when the bottom of the table passes above the top of the screen; and x-axis scrolling moves the cloned header sideways because position:fixed is, well, fixed.
sets a watcher for when the the window is resized to keep the widths of the cloned table in sync with tblData
<html>
<script type="text/javascript" src="jquery.min.js"></script><!-- load before freeze header -->
<script type="text/javascript">
var distance = 0, $window = $(window);
function resizeHdr(){
$("#tblDataHdr").css("width", $("#tblData").css("width"));
var oTr=$("#tblDataHdrTr").html("");//short var for frozen header row
$("#tblData tr").first().find("th").each(function(i){//loop through header to mimic
oTr.append('<th style="width:'+$(this).css("width")+' !important">'+$(this).html()+'</th>');//copy content with forced width
});
}
$(function(){
$("body").prepend(// stuff frozen header table html into the page just inside/at top of <body>
'<table class="'+$("#tblData").attr("class")+
'" style="display:none;position:fixed;background-color:white;z-index:10;'+$("#tblData").attr("style")+
'" id="tblDataHdr">'+
'<tr id="tblDataHdrTr" class="" style=""></tr></table>'
)
distance=$('#tblData').offset().top;
var oTr=$("#trHoldHdr");//stuff the frozen header
$("#tblDataHdr").css({"margin-left":$("body").css("margin"),"margin-top":-parseInt($("body").css("margin"))+"px"});//position frozen hder
$("#tblData tr:first").find("th").each(function(i){//populate <th>
oTr.append("<th>"+$(this).html()+"</th>");
});
resizeHdr();
});
$(window).scroll(function(){
$("#tblDataHdr").css("left", -$(this).scrollLeft());//frozen hdr pos fixed doesn't move w/ scrolling so this keeps it lined up (w/o "-" header slides twice as fast out to the right)
if($window.scrollTop() >= distance &&
$window.scrollTop() <= distance + 1*$("#tblData").css("height").replace(/px/,"")){
$("#tblDataHdr").css("display","");// Hdr has reached the top
}else{
$("#tblDataHdr").css("display","none");// Hdr below top
}
});
$(window).resize(function(){
resizeHdr();
});
</script>
<body>
<table id="tblData"><thead>
<tr><th><b style="color:red;">col 1</b></th><th>Linked Wide Column Header</th><tr>
</thead><tbody>
<tr><td>1234567890 1234567890 1234567890</td><td>xyz</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
<tr><td>.</td><td>.</td></tr>
</tbody></table>
<div style="height:300px;">Below table</div>
</body>
</html>