I'd like to create a responsive left-hand menu, like the menu on Yahoo's Pure CSS site.
In other words, it should look like this on desktop:
And collapse like this on narrower screens:
Oddly, although this menu is prominent on the Pure site, it doesn't actually seem to be part of the Pure framework.
I've been struggling with the CSS to replicate it, looking at Yahoo's source - this is as far as I've got: http://jsfiddle.net/WZt4z/
It's part-way there, but I don't understand how they have styled the body of the page so it's in the right place, and got rid of the scrollbars on the menu.
Here is the HTML in the JSFiddle:
<a href="#menu" id="menuLink" class="pure-menu-link">
<img src="/img/navicon-png2x.png" width="20" alt="Menu toggle">
</a>
<div class="pure-u" id="menu"> <!-- contents of menu --> </div>
<div class="pure-u-1" id="main"> <!-- contents of body of page --> </div>
And the CSS:
#menu {
margin-top: 31px;
margin-left: -150px; /* "#menu" width */
width: 150px;
position: fixed;
top: 0;
left: 150px;
bottom: 0;
z-index: 1000; /* so the menu or its navicon stays above all content */
background: grey;
overflow-y: auto;
-webkit-overflow-scroll: touch;
}
.pure-menu-link {
display: none; /* show this only on small screens */
top: 0;
left: 150px; /* "#menu width" */
background: #000;
background: rgba(0,0,0,0.7);
padding: 0.75em 1em;
}
#media (max-width: 470px)
... responsive styles
You're running into problems because the side menu layout they built has multiple classes and ids that you are not including. Specifically you need the "layout" id:
#layout {
padding-left: 150px; /* left col width "#menu" */
left: 0;
}
Additionally for the menu to work properly you need to include the javascript for it:
(function (window, document) {
var layout = document.getElementById('layout'),
menu = document.getElementById('menu'),
menuLink = document.getElementById('menuLink');
function toggleClass(element, className) {
var classes = element.className.split(/\s+/),
length = classes.length,
i = 0;
for(; i < length; i++) {
if (classes[i] === className) {
classes.splice(i, 1);
break;
}
}
// The className is not found
if (length === classes.length) {
classes.push(className);
}
element.className = classes.join(' ');
}
menuLink.onclick = function (e) {
var active = 'active';
e.preventDefault();
toggleClass(layout, active);
toggleClass(menu, active);
toggleClass(menuLink, active);
};
}(this, this.document));
The javascript uses the ids to update the css when you hit the media breakpoint (screen gets too small) so if you don't have all the necessary ids ("layout", "menu", "menuLink") the javascript will break as well.
I updated the fiddle you posted with the necessary code (I pulled it straight from the site at purecss.io).
Here's the working fiddle: http://jsfiddle.net/schmanarchy/WZt4z/3/
Related
Here is an example chat app ->
The idea here is to have the .messages-container take up as much of the screen as it can. Within .messages-container, .scroll holds the list of messages, and in case there are more messages then the size of the screen, scrolls.
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Now, instead of the user staying scrolled to the bottom of the conversation, the text-input increases, and they no longer see the bottom.
One way to fix it, if we are using react, calculate the height of text-input, and if anything changes, let .messages-container know
componentDidUpdate() {
window.setTimeout(_ => {
const newHeight = this.calcHeight();
if (newHeight !== this._oldHeight) {
this.props.onResize();
}
this._oldHeight = newHeight;
});
}
But, this causes visible performance issues, and it's sad to be passing messages around like this.
Is there a better way? Could I use css in such a way, to express that when .text-input-increases, I want to essentially shift up all of .messages-container
2:nd revision of this answer
Your friend here is flex-direction: column-reverse; which does all you ask while align the messages at the bottom of the message container, just like for example Skype and many other chat apps do.
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
The downside with flex-direction: column-reverse; is a bug in IE/Edge/Firefox, where the scrollbar doesn't show, which your can read more about here: Flexbox column-reverse and overflow in Firefox/IE
The upside is you have ~ 90% browser support on mobile/tablets and ~ 65% for desktop, and counting as the bug gets fixed, ...and there is a workaround.
// scroll to bottom
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
// only shift-up if at bottom
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
In the below code snippet I've added the 2 functions from above, to make IE/Edge/Firefox behave in the same way flex-direction: column-reverse; does.
function addContent () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.value.length > 0) {
msgdiv.innerHTML += msgtxt.value + '<br/>';
msgtxt.value = "";
} else {
msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
function resizeInput () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.style.height == '120px') {
msgtxt.style.height = 'auto';
} else {
msgtxt.style.height = '120px';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
/* fix for IE/Edge/Firefox */
var isWebkit = ('WebkitAppearance' in document.documentElement.style);
var isEdge = ('-ms-accelerator' in document.documentElement.style);
var tempCounter = 6;
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
html, body { height:100%; margin:0; padding:0; }
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
/* temp. buttons for demo */
button { width: 12%; height: 44px; margin-left: 5%; vertical-align: top; }
/* begin - fix for hidden scrollbar in IE/Edge/Firefox */
.chat-messages-text{ overflow: auto; }
#media screen and (-webkit-min-device-pixel-ratio:0) {
.chat-messages-text{ overflow: visible; }
/* reset Edge as it identifies itself as webkit */
#supports (-ms-accelerator:true) { .chat-messages-text{ overflow: auto; } }
}
/* hide resize FF */
#-moz-document url-prefix() { .chat-input-text { resize: none } }
/* end - fix for hidden scrollbar in IE/Edge/Firefox */
<div class="chat-window">
<div class="chat-messages">
<div class="chat-messages-text" id="messages">
Long long content 1!<br/>
Long long content 2!<br/>
Long long content 3!<br/>
Long long content 4!<br/>
Long long content 5!<br/>
</div>
</div>
<div class="chat-input">
<textarea class="chat-input-text" placeholder="Type your message here..." id="inputs"></textarea>
<button onclick="addContent();">Add msg</button>
<button onclick="resizeInput();">Resize input</button>
</div>
</div>
Side note 1: The detection method is not fully tested, but it should work on newer browsers.
Side note 2: Attach a resize event handler for the chat-input might be more efficient then calling the updateScroll function.
Note: Credits to HaZardouS for reusing his html structure
You just need one CSS rule set:
.messages-container, .scroll {transform: scale(1,-1);}
That's it, you're done!
How it works: First, it vertically flips the container element so that the top becomes the bottom (giving us the desired scroll orientation), then it flips the content element so that the messages won't be upside down.
This approach works in all modern browsers. It does have a strange side effect, though: when you use a mouse wheel in the message box, the scroll direction is reversed. This can be fixed with a few lines of JavaScript, as shown below.
Here's a demo and a fiddle to play with:
//Reverse wheel direction
document.querySelector('.messages-container').addEventListener('wheel', function(e) {
if(e.deltaY) {
e.preventDefault();
e.currentTarget.scrollTop -= e.deltaY;
}
});
//The rest of the JS just handles the test buttons and is not part of the solution
send = function() {
var inp = document.querySelector('.text-input');
document.querySelector('.scroll').insertAdjacentHTML('beforeend', '<p>' + inp.value);
inp.value = '';
inp.focus();
}
resize = function() {
var inp = document.querySelector('.text-input');
inp.style.height = inp.style.height === '50%' ? null : '50%';
}
html,body {height: 100%;margin: 0;}
.conversation {
display: flex;
flex-direction: column;
height: 100%;
}
.messages-container {
flex-shrink: 10;
height: 100%;
overflow: auto;
}
.messages-container, .scroll {transform: scale(1,-1);}
.text-input {resize: vertical;}
<div class="conversation">
<div class="messages-container">
<div class="scroll">
<p>Message 1<p>Message 2<p>Message 3<p>Message 4<p>Message 5
<p>Message 6<p>Message 7<p>Message 8<p>Message 9<p>Message 10<p>Message 11<p>Message 12<p>Message 13<p>Message 14<p>Message 15<p>Message 16<p>Message 17<p>Message 18<p>Message 19<p>Message 20
</div>
</div>
<textarea class="text-input" autofocus>Your message</textarea>
<div>
<button id="send" onclick="send();">Send input</button>
<button id="resize" onclick="resize();">Resize input box</button>
</div>
</div>
Edit: thanks to #SomeoneSpecial for suggesting a simplification to the scroll code!
Please try the following fiddle - https://jsfiddle.net/Hazardous/bypxg25c/. Although the fiddle is currently using jQuery to grow/resize the text area, the crux is in the flex related styles used for the messages-container and input-container classes -
.messages-container{
order:1;
flex:0.9 1 auto;
overflow-y:auto;
display:flex;
flex-direction:row;
flex-wrap:nowrap;
justify-content:flex-start;
align-items:stretch;
align-content:stretch;
}
.input-container{
order:2;
flex:0.1 0 auto;
}
The flex-shrink value is set to 1 for .messages-container and 0 for .input-container. This ensures that messages-container shrinks when there is a reallocation of size.
I've moved text-input within messages, absolute positioned it to the bottom of the container and given messages enough bottom padding to space accordingly.
Run some code to add a class to conversation, which changes the height of text-input and bottom padding of messages using a nice CSS transition animation.
The JavaScript runs a "scrollTo" function at the same time as the CSS transition is running to keep the scroll at the bottom.
When the scroll comes off the bottom again, we remove the class from conversation
Hope this helps.
https://jsfiddle.net/cnvzLfso/5/
var doScollCheck = true;
var objConv = document.querySelector('.conversation');
var objMessages = document.querySelector('.messages');
var objInput = document.querySelector('.text-input');
function scrollTo(element, to, duration) {
if (duration <= 0) {
doScollCheck = true;
return;
}
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) {
doScollCheck = true;
return;
}
scrollTo(element, to, duration - 10);
}, 10);
}
function resizeInput(atBottom) {
var className = 'bigger',
hasClass;
if (objConv.classList) {
hasClass = objConv.classList.contains(className);
} else {
hasClass = new RegExp('(^| )' + className + '( |$)', 'gi').test(objConv.className);
}
if (atBottom) {
if (!hasClass) {
doScollCheck = false;
if (objConv.classList) {
objConv.classList.add(className);
} else {
objConv.className += ' ' + className;
}
scrollTo(objMessages, (objMessages.scrollHeight - objMessages.offsetHeight) + 50, 500);
}
} else {
if (hasClass) {
if (objConv.classList) {
objConv.classList.remove(className);
} else {
objConv.className = objConv.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
}
}
}
}
objMessages.addEventListener('scroll', function() {
if (doScollCheck) {
var isBottom = ((this.scrollHeight - this.offsetHeight) === this.scrollTop);
resizeInput(isBottom);
}
});
html,
body {
height: 100%;
width: 100%;
background: white;
}
body {
margin: 0;
padding: 0;
}
.conversation {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
position: relative;
}
.messages {
overflow-y: scroll;
padding: 10px 10px 60px 10px;
-webkit-transition: padding .5s;
-moz-transition: padding .5s;
transition: padding .5s;
}
.text-input {
padding: 10px;
-webkit-transition: height .5s;
-moz-transition: height .5s;
transition: height .5s;
position: absolute;
bottom: 0;
height: 50px;
background: white;
}
.conversation.bigger .messages {
padding-bottom: 110px;
}
.conversation.bigger .text-input {
height: 100px;
}
.text-input input {
height: 100%;
}
<div class="conversation">
<div class="messages">
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is the last message
</p>
<div class="text-input">
<input type="text" />
</div>
</div>
</div>
You write;
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Wouldn't the method that dynamically sets the .text-input be the logical place to fire this.props.onResize().
To whom it may concern,
The answers above did not suffice my question.
The solution I found was to make my innerWidth and innerHeight variable constant - as the innerWidth of the browser changes on scroll to adapt for the scrollbar.
var innerWidth = window.innerWidth
var innerHeight = window.innerHeight
OR FOR REACT
this.setState({width: window.innerWidth, height: window.innerHeight})
In other words, to ignore it, you must make everything constant as if it were never scrolling. Do remember to update these on Resize / Orientation Change !
IMHO current answer is not a correct one:
1/ flex-direction: column-reverse; reverses the order of messages - I didn't want that.
2/ javascript there is also a bit hacky and obsolete
If you want to make it like a PRO use spacer-box which has properties:
flex-grow: 1;
flex-basis: 0;
and is located above messages. It pushes them down to the chat input.
When user is typing new messages and input height is growing the scrollbar moves up, but when the message is sent (input is cleared) scrollbar is back at bottom.
Check my snippet:
body {
background: #ccc;
}
.chat {
display: flex;
flex-direction: column;
width: 300px;
max-height: 300px;
max-width: 90%;
background: #fff;
}
.spacer-box {
flex-basis: 0;
flex-grow: 1;
}
.messages {
display: flex;
flex-direction: column;
overflow-y: auto;
flex-grow: 1;
padding: 24px 24px 4px;
}
.footer {
padding: 4px 24px 24px;
}
#chat-input {
width: 100%;
max-height: 100px;
overflow-y: auto;
border: 1px solid pink;
outline: none;
user-select: text;
white-space: pre-wrap;
overflow-wrap: break-word;
}
<div class="chat">
<div class="messages">
<div class="spacer-box"></div>
<div class="message">1</div>
<div class="message">2</div>
<div class="message">3</div>
<div class="message">4</div>
<div class="message">5</div>
<div class="message">6</div>
<div class="message">7</div>
<div class="message">8</div>
<div class="message">9</div>
<div class="message">10</div>
<div class="message">11</div>
<div class="message">12</div>
<div class="message">13</div>
<div class="message">14</div>
<div class="message">15</div>
<div class="message">16</div>
<div class="message">17</div>
<div class="message">18</div>
</div>
<div class="footer">
<div contenteditable role="textbox" id="chat-input"></div>
</div>
<div>
Hope I could help :)
Cheers
I'd like to implement resizable panes without JS, only using CSS Grid layout. Is this possible?
For e.g. CodePen's editor has resizable panes for its HTML / CSS / JS editors. Also, see the example below which implements it in plain JS (I can't seem to add a URL to the CodePen example in it, so it's a bit hard to add attribution. The code is by http://codepen.io/gsound/).
let isResizing = false;
let $handler = document.getElementById('handler');
let $wrapper = document.getElementById('wrapper');
let $left = document.getElementById('left');
$handler.addEventListener('mousedown', function(e){
isResizing = true;
});
document.addEventListener('mousemove', function(e){
if( !isResizing ) return;
let newWidth = e.clientX - $wrapper.offsetLeft;
$left.style.width = newWidth + 'px';
});
document.addEventListener('mouseup', function(){
isResizing = false;
});
html,body{
height: 100%;
width: 100%;
margin: 0;
}
#app{
height: 100%;
/* max-width: 1400px; */
margin: 0 auto;
}
header{
background-color: #AAA;
height: 50px;
}
#wrapper{
margin: 0 auto;
height: calc(100% - 50px);
width: 100%;
background-color: #EEE;
display: flex;
}
#handler{
background-color: red;
width: 5px;
cursor: col-resize;
}
#left{
width: 200px;
}
#content{
width: 100%;
flex: 1;
}
/* ----------- */
#left{
background-color: yellow;
}
#content{
background-color: #232378;
}
<div id="app">
<header>head</header>
<div id="wrapper">
<aside id="left"></aside>
<div id="handler"></div>
<article id="content"></article>
</div>
</div>
P.S as a side note, I have re-implemented the e.g. above by adding and removing 'mousemove', 'mouseup' events and was wondering whether that's "better" (more performant) than using a boolean isResizing and keeping the event listeners always there...
The Grid Layout module is pure CSS.
Resizable panes generally require JavaScript to function.
Knowing that CSS is designed primarily for styling and presentation, there are most likely no built-in properties or methods in the Grid Layout spec that would provide for manually resizable panes.
I am displaying the wordpress posts on my blog index page in a masonry layout. I also have the masonry activated on footer, to display the wordpress footer widgets in a masonry layout.
So basically I have 2 masonry containers on the same page.
1. One for displaying the blog posts, and
2. Other for displaying the footer widgets.
The html markup for the page looks like this:
HTML
<!-- Posts -->
<main id="main">
<div class="testmason">
<article class="hentry">Post 1</article>
<article class="hentry">Post 2</article>
<article class="hentry">Post 3</article>
.....
.....
<article class="hentry">Post n</article>
</div>
</main>
<!-- Footer Widgets -->
<div id="footer-widgets" class="footer-widgets">
<aside class="widget">Widget 1</aside>
<aside class="widget">Widget 2</aside>
<aside class="widget">Widget 3</aside>
.....
.....
<aside class="widget">Widget n</aside>
</div>
Following is the url where I am trying to implement this layout. -- http://lanarratrice-al-rawiya.com/lanarratrice/blog/
I dont want the masonry to load on the mobile devices.
1. I want the masonry on Posts to work only when the min width of the document is 837px.
2. Also I want the masonry on Footer to work only when the min width of the document is 880px.
Any media query lower than the above width(s) will not trigger the masonry layout, and I will display all my posts and widgets in a full width (taking up the full space available). To implement this I am using enquire js, that will trigger the masonry layout if it matches the media query. Following is my javascript:
JAVASCRIPT
// Masonry settings to organize footer widgets and blog posts
jQuery(document).ready(function($){
var $container = $('#footer-widgets'),
$maincontent = $('.blog .testmason');
enquire.register("screen and (min-width:837px)", {
// Triggered when a media query matches.
match : function() {
$maincontent.masonry({
columnWidth: 200,
itemSelector: '.hentry',
isFitWidth: true,
isAnimated: true
});
},
// Triggered when the media query transitions
// from a matched state to an unmatched state.
unmatch : function() {
$maincontent.masonry('destroy');
}
});
enquire.register("screen and (min-width:880px)", {
// Triggered when a media query matches.
match : function() {
$container.masonry({
columnWidth: 400,
itemSelector: '.widget',
isFitWidth: true,
isAnimated: true
});
},
// Triggered when the media query transitions
// from a matched state to an unmatched state.
unmatch : function() {
$container.masonry('destroy');
}
});
});
And finally this is my css that is being applied on this page:
CSS
#main {
max-width: 100%;
width: 100%;
}
.testmason {
margin: 0 auto 35px;
}
#main article {
margin: 0 20px 35px;
width: 360px;
float: left;
}
#media screen and (max-width: 854px) {
#main { width: 100%; }
#main .testmason {
margin: 0 10px;
width: auto!important;
height: auto!important;
}
#main article {
float: none;
width: 100%;
margin: 0 0 35px;
}
#main .index-box {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.22);
margin: 0 auto 2em;
width: auto;
max-width: 780px;
max-width: 78rem;
}
#main .navigation {
width: auto;
max-width: 780px;
max-width: 78rem;
}
}
.index-box {
margin: 0;
}
nav.paging-navigation {
width: 90%;
position: static!important;
margin: 0 auto;
}
It seems that my javascript is working correctly, because the masonry layout is implemented. But as I resize my firefox browser window to about 846px (approx around this size), I see a broken layout. I see sometimes that the post is on top of footer. Please see this following picture attached.
To reproduce this bug you might have to shrink and expand your browser window (firefox) around 5-8 times. Sometimes if you shrink it very fast or very slow you might not see the broken layout. BTW I am using Firefox 35.0.1.
Please let me know what can I do to fix this issue. Thanks.
Add the $container.masonry() function on page load too.
The issue is, it registers the event on resize, but on load it fails to calculate the body heights to calculate the positions. As these post blocks are absolutely positioned, it bleeds over footer.
I am working on a webpage using jQuery Mobile, iScrollview, and Google Maps. When the map gets initialized, it will load a set of markers with infowindows containing an image. When the image gets clicked, it will load the content of a jQM popup with a list and show the popup.
I am having 2 problems:
1) How can I set a height (e.g. 150px) for my jQM popup's content? Using iScrollview, the content's height becomes overridden and becomes large. Using the refresh() as specified in the documentation has no effect (or am I using it wrong?).
2) Is there a way for me to show a scrollbar only if the list exceeds the height of the content? Currently, it always shows a scrollbar.
HTML
<div style="display: none;">
<div class="popup">
<div class="header" data-role="header">
<h1>Products</h1>
</div>
<div class="content" data-role="content" data-iscroll>
</div>
<div class="footer" data-role="footer">
<a class="close" href="#" data-rel="back" data-role="button">Close</a>
</div>
</div>
</div>
CSS
.popup {
width: 175px;
}
.popup .content {
height: 150px; /* has no effect */
padding: 10px;
}
.popup .content ul {
list-style-type: none;
margin: 0px;
padding: 0px;
}
.popup .content ul li {
height: 23px;
line-height: 23px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
JavaScript
var markers = [];
function addMarker(i, retailer) {
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(retailer.lat, retailer.lng),
products: retailer.products // array of strings of unknown length
});
var infobox = new InfoBox({
boxClass: 'infobox',
closeBoxURL: '',
content: '\
<img class="detail" src="img/arrow_light.png" alt="" onclick="openPopup('+ parseInt(i) +');" />\
<div class="name"><span>' + retailer.name + '</span></div>\
<div class="address"><span>' + retailer.address + '</span></div>\
<div class="arrow"></div>',
maxWidth: 500,
pixelOffset: new google.maps.Size(0, -110)
});
markers.push(marker);
}
function openPopup(i) {
var items = [];
// create list
$.each(markers[i].products, function(i, name) {
items.push('<li>' + name + '</li>');
});
// set popup's content
$('.popup .content')
.empty()
.append('<ul class="list">' + items.join('') + '</ul>')
.iscrollview('refresh');
// show popup
$('.popup').popup({ history: false, transition: 'pop' }).popup('open');
}
Only thing you need to do is override popup content height and inforce it like this:
.ui-popup .ui-content {
height: 150px !important;
}
Remember to use !important if you want to override class css. !important is only needed when working with classes. For example if you initialize your css through id (#) !important is not needed.
Working example: http://jsfiddle.net/Gajotres/N28Vg/
I'm trying to set a max-height to an image. It works well in Safari and Chrome, but not in Firefox/Opera/IE. Now I read that html and body heights should be put at 100%, and it did work when I used jsfiddle. However, it doesn't work in my page (memo-designs.com/portfolio.php).
The following is the source of the page:
<!DOCTYPE html>
<html>
<head>
<title>memodesigns</title>
<link rel='stylesheet' href='style/stylesheet.css'>
<script type = 'text/javascript'>
function displayImage(image, link) {
document.getElementById('img').src = image;
document.getElementById('mylink').href = link;
}
function displayNextImage() {
if (x < images.length-1){
x++;
} else {
x = 0;
}
displayImage(images[x], links[x]);
}
function displayPreviousImage() {
if (x > 0){
x--;
} else {
x = images.length-1;
}
displayImage(images[x]);
}
function startTimer() {
setInterval(displayNextImage, -1);
}
var images = [], links = [], x = 0;images[0] = "http://memo-designs.com/items/doublek-01.png"
links[0] = "http://memo-designs.com/items/doublek-01.png"
images[1] = "http://memo-designs.com/items/memodesigns.png"
links[1] = "http://memo-designs.com/items/memodesigns.png"
</script>
</head>
<body style = 'background-color: #000000'><div id = 'menucontainer'>
<div id = 'menu'>
<p>
<ul>
<li><a class = 'menu' href = '/'>HOME</a></li>
<li><a class = 'menu' href = 'about.php'>ABOUT</a></li>
<li><a class = 'menu' href = 'portfolio.php'>PORTFOLIO</a></li>
<li><a class = 'menu' href = 'rates.php'>RATES</a></li>
<li><a class = 'menu' href = 'contact.php'>CONTACT</a></li>
</ul>
</p>
</div>
</div>
<div id = 'contentcontainer' style = 'padding-top: 0%; max-height: 100%; overflow: hidden; background-color: #000000'>
<p>
<img id= 'img' src = 'http://memo-designs.com/items/doublek-01.png' style = 'max-height: 100%; max-width: 100%; display: block; margin-left: auto; margin-right: auto;'>
<img class = 'arrow' onclick = 'displayPreviousImage()' id= 'img' src = 'style/graphics/larrow.png' style = 'position: absolute; left: 0; top: 40%;'>
<img class = 'arrow' onclick = 'displayNextImage()' id= 'img' src = 'style/graphics/rarrow.png' style = 'position: absolute; right: 0; top: 40%;'> </p>
</div>
</body>
</html>
And the css stylesheet (only part of it is shown here):
*{
margin: 0;
padding: 0;
}
html{
margin: 0;
min-width: 100%;
height: 100%;
min-height: 100%;
}
body{
margin: 0px;
background-color: #f3f4f4;
min-width: 100%;
height: 100%;
min-height: 100%;
}
Would appreciate any help as to what I'm doing wrong :)
First of all I recommend you to start using a CSS-Reset like Normalize.css . It makes browsers render all elements more consistently and in line with modern standards.
Your HTML notation might also cause inconsistency across browsers. Turn things like <div id = 'menu'> into <div id="menu">. This makes it also more readable IMHO.
Inline style attributes make maintaining the pages a pain and may override things you didn't intent to. They also need to be applied to every single element thus also increasing download time. Using classes / id's is the way to go. Also pseudo-elements can't be used with inline styles. I advice to only use them for quick changes during development. I use the element inspector from Chrome / Firefox to change things quickly and instantly see how the changes look, copy/pasting the edits afterwards.
So, make sure to put all css into your stylesheet. It's also considered as a best practice for maintainability and better download speed (minify the files for production) of your pages.
You surely have heard about jQuery before. Try using it. jQuery makes developing things like image sliders a breeze (once you understand the syntax, but it's a low learning curve). Furthermore, there are LOTS of ready-to-use plugins for jQuery.
Another "good practice" is to put your javascripts at the very end of your document just before the </body> tag. Read more about this here and here.
Ok, enough tips. Let's get the hands dirty:
The HTML Part:
<!DOCTYPE html>
<html>
<head>
<title>memodesigns</title>
<link rel="stylesheet" href="/assets/css/normalize.css">
<link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
<div id="menuContainer">
<div id="menu">
<p>
<ul>
<!-- Instead of writing in CAPITALS use the text-transform:uppercase; css property -->
<li><a class="menu" href="/">Home</a></li>
<li><a class="menu" href="about.php">About</a></li>
<li><a class="menu" href="portfolio.php">Portfolio</a></li>
<li><a class="menu" href="rates.php">Rates</a></li>
<li><a class="menu" href="contact.php">Contact</a></li>
</ul>
</p>
</div>
</div>
<div id="contentContainer">
<p>
<!-- NOTE: Use IDs only once, else use classes to share css styles -->
<img id="img" src="http://memo-designs.com/items/doublek-01.png">
<img class="arrow left" src="style/graphics/larrow.png" onclick="displayPreviousImage()">
<img class="arrow right" src="style/graphics/rarrow.png" onclick="displayNextImage()">
</p>
</div>
<!-- Put the JavaScript at the end of the document just before the closing body tag -->
<script>
var images = [], links = [], x = 0,
baseUrl = "http://memo-designs.com/items/";
images[0] = baseUrl + "doublek-01.png";
links[0] = baseUrl + "doublek-01.png";
images[1] = baseUrl + "memodesigns.png";
links[1] = baseUrl + "memodesigns.png";
function displayImage(img, link)
{
document.getElementById('img').src = img;
document.getElementById('mylink').href = link;
}
function displayNextImage()
{
if (x < images.length-1) x++;
else x = 0;
displayImage(images[x], links[x]);
}
function displayPreviousImage()
{
if (x > 0) x--;
else x = images.length-1;
displayImage(images[x]);
}
function startTimer()
{
setInterval(displayNextImage, -1);
}
</script>
</body>
</html>
...and the CSS:
/* Assuming you'll use a CSS-Reset */
body {
background-color: #f3f4f4;
font:
...
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#menuContainer { ... }
#menu { ... }
#menu ul { ... }
/* Making the menu labels all UPPERCASE */
#menu ul > li {
text-transform: uppercase;
}
#contentContainer {
background-color: #000;
padding-top: 0;
overflow: hidden;
/* IMPORTANT: Set a fixed pixel height here to make the images use up the given space */
height: 200px; /* change 200 to your needs */
}
#img {
display: block;
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
}
#contentContainer .arrow {
position: absolute;
top: 40%;
}
#contentContainer .arrow.left {
left: 0;
}
#contentContainer .arrow.right {
right: 0;
}
Ok, try out the suggestions and the code example. Tell us if and what helped.
Good luck and happy coding!