As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
I have never used a lightbox before, but recently started thinking about using a similar feature on my website. I found a ton of jquery libraries and various add-ons, but I am a big fan of writing my own code. When I started looking under the hood, I was surprised to realize that it appears to simply be a hidden html element that is displayed when a Javascript event listener is triggered. Am I right about this? Is there more than meets the eye?
Just wondering how it works. Thank you in advance for your consideration.
UPDATE
Great answers! I went with Ana's response because the other considerations regarding the design go without saying. As far as the mechanisms, it really does appear to be a wonderfully understated device. Thanks to all who read, commented, and replied...
No, there really is not much more to it. However, I don't really see the point of having the HTML element there from the beginning unless you don't want to use JavaScript for your lightbox, which I don't think it is the case here judging from your tags and which I'm not a fan of using in real projects, even though I think it is really cool that it can be done.
If you're using JavaScript anyway in order to display the lightbox (which means that if JavaScript is disabled, your lightbox won't get displayed even if it is there, loaded right from the beginning... so why load it if you can't display it?), then it's probably better to just create the lightbox element (and everything in it, and then append it to the page) only when you first want the lightbox to open.
What I mean by that is that you attach a click handler to the links (actually, I would attach it to the container an then check what was clicked, if it is a link, see what link it is and go further) and check whether you have a lightbox element. If you don't, then you create it on the spot. If you already do, then you simply display it with whatever you need in it for that particular link that was clicked.
A basic lightbox example for an image gallery.
The HTML structure of the gallery would be:
<section class='gallery' id='gallery'>
<a href='images/large_0.jpg' class='thumb'>
<img class='thumb-img' src='images/small_0.jpg'>
</a>
<!-- as many more as you wish -->
</section>
The CSS:
/* gallery with thumbnails */
.gallery { text-align: center; }
.gallery .thumb, .gallery .thumb-img {
display: inline-block;
width: 10em;
height: 5.6em;
}
/* lightbox */
.lightbox {
z-index: 999; /* some ridiculously large value to make sure it's on top */
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
background: rgba(0,0,0,.5);
cursor: pointer;
text-align: center;
}
.lightbox:before { /* strictly for vertical centering of large image */
display: inline-block;
height: 100%;
vertical-align: middle;
content: '';
}
/* add/ remove this class to toggle display */
.hidden { display: none; }
.large { /* the large image */
max-width: 100%;
max-height: 100%;
vertical-align: middle;
}
The JavaScript:
var g = document.getElementById('gallery');
String.prototype.endsIn = function(suffixes) { /*just to check the extension*/
for(i in suffixes) {
if(this.indexOf(suffixes[i], this.length - suffixes[i].length) !== -1)
return true;
}
return false;
};
g.addEventListener('click', function(e){
var target = e.target, lnk, ext = ['.jpg', '.png'], lightbox, large;
if(!target.classList.contains('thumb-img')) return;
else {
lnk = target.parentNode.href;
if(!lnk.endsIn(ext)) return;
else {
lightbox = document.getElementById('lightbox');
if(lightbox == null) {
lightbox = document.createElement('div');
lightbox.setAttribute('id', 'lightbox');
lightbox.classList.add('lightbox');
lightbox.innerHTML = "<img src='"+ lnk +"' id='large' class='large'>";
document.body.appendChild(lightbox);
lightbox.addEventListener('click', function(ev) {
var target = ev.target, next, links = g.querySelectorAll('.thumb'),
len = links.length, large = document.getElementById('large');
if(target.id == 'lightbox') lightbox.classList.add('hidden');
else if(target.id == 'large') {
for(var i = 0; i < len; i++) {
if(links[i].href == large.src) {
next = links[(i++)%len].href;
while(!next.endsIn(ext)) next = links[(i++)%len].href;
large.src = links[i%len].href;
break;
}
}
}
}, false);
}
else {
lightbox.classList.remove('hidden');
large = document.getElementById('large');
large.src = lnk;
}
e.preventDefault();
}
}
}, false);
Related
I have a simple JS scroll event that when an element gets to within 50px of the top of the window the header animates and changes colour, which is done by using getBoundingClientRect().top < 50 on a trigger element. This functionality is only on the home page of the site.
Is there anyway of having it so when a user visits another URL/page on the site, and then comes back to this page via the browsers back arrow, that the previous animation state is still applied? If the page reloads and starts at the top again it doesn't matter, but if you click back to the page that uses this code, the menu transition happens even if you return to part of the page that was past the trigger point. I don't want to force the page to the top each time because this page is going to have downloadable and searchable info on, so that it would be real pain to be sent back to the top of that page each time.
I've given a working example below and via the CodePen link, the problem is of course on CodePen and StackOverflow when you go to a different URL and then click back to URL in question it actually reloads the page from scratch again, which doesn't happen as standard browser behaviour on day-to-day websites.
Codepen: https://codepen.io/anna_paul/pen/bGvPWRj
In that back end I'm using PHP, and I do have access to this is there needs to be a server side solution.
Any ideas or suggestions appreciated.
Note: On the actual site this scroll event is invoked via a debounce function, but I have removed this for code simplicity.
let triggerElement = document.getElementById('trigger-element'),
header = document.getElementById('h')
let menuChange = function() {
if(triggerElement.getBoundingClientRect().top < 50) {
header.style.background = 'black'
header.style.transition = '1s'
} else {
header.style.background = 'red'
header.style.transition = '.15s'
}
}
window.addEventListener('scroll', menuChange)
body {
margin: 0;
height: 200vh;
}
#h {
display: flex;
justify-content: center;
background: red;
color: #fff;
position: fixed;
top: 0;
width: 100%;
}
#trigger-element {
margin-top: 150px;
padding: 1rem;
background:blue;
color: #fff;
}
<header id="h">
<p>HEADER CONTENT</p>
</header>
<div id="trigger-element">Trigger Element</div>
I recommend using localStorage for this particular use case, because it can easily be implemented alongside your current method:
const triggerElement = document.getElementById('trigger-element');
const header = document.getElementById('h');
const animationTriggered = localStorage.getItem('animationTriggered') === 'true';
let initialLoad = true;
const menuChange = function() {
if (animationTriggered && initialLoad) {
header.style.background = 'black';
} else if (triggerElement.getBoundingClientRect().top < 50) {
header.style.background = 'black';
header.style.transition = '1s';
localStorage.setItem('animationTriggered', 'true');
} else {
header.style.background = 'red';
header.style.transition = '.15s';
localStorage.setItem('animationTriggered', 'false');
}
initialLoad = false;
}
window.addEventListener('scroll', menuChange);
This will remember the previous state and apply the black background color if the animation was previously triggered. This adds a small amount of overhead, but in a real-world scenario it should not have any noticeable impact on the performance of the application.
I made a menu on html (on the side and 100% heigth, expandeable as in android holo)
<div id="menu">
<button class="menubutton"></button>
<button class="menubutton"></button>
</div>
The menu normally remains transparent and with a short width:
#menu {
background-color: transparent;
width: 8%;
}
The idea was to expand and color it on hover. It was easy:
#menu:hover {
background-color: blue;
width: 90%;
}
There is no problem untill here. I need the same effect on focus. There is no way in css to change parent css on child focus (neither hover by the way, but it is not needed, cuase i can use the entire menu hover).
So i used a script:
var menubuttonfocus = document.getElementsByClassName("menubutton");
for (i=0; i<menubuttonfocus.length; i++) {
menubuttonfocus[i].addEventListener("focus", function() {
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
});
menubuttonfocus[i].addEventListener("blur", function() {
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
});
}
The script works just fine, the problem is that when you trigger those events by focusing a button, the css of #menu:hover changes somehow and #menu does not change when hovering. I tried to solve this by doing something similar but with hover instead of focus:
menu.addEventListener("mouseenter", function(){
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
});
menu.addEventListener("mouseout", function(){
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
});
This works somehow, but it is REALLY buggy.
I tried also to select "#menu:hover,#menu:focus", but it doesn't work because the focus is on the button elements and not in #menu.
Please avoid jquery if posible, and i know it's asking for too much but a pure css solution would be awesome.
Probably helpful info: html element are created dinamically with javascript.
I can show more code or screenshot, you can even download it (it is a chrome app) if needed: chrome webstore page
Thanks.
SOLVED: I did what #GCyrillus told me, changing #menu class on focus via javascript eventListener. .buttonbeingfocused contains the same css as "#menu:hover". Here is the script:
var menubuttonfocus = document.getElementsByClassName("menubutton");
for (i=0; i<menubuttonfocus.length; i++) {
menubuttonfocus[i].addEventListener("focus", function() {
menu.classList.add("buttonbeingfocused");
});
menubuttonfocus[i].addEventListener("blur", function() {
menu.classList.remove("buttonbeingfocused");
});
}
if the problem is what I think it is - you forgetting about one thing:
When you focusing / mouseentering the .menubutton - you are mouseleaving #menu and vice-versa - so your menu behaviour is unpredictible because you want to show your menu and hide it at the same time.
solution is usually setting some timeout before running "hiding" part of the script, and clearing this timeout (if exist) when running "showing" part.
it will be something like this:
var menuTimeout;
function showMenu() {
if (menuTimeout) clearTimeout(menuTimeout);
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
}
function hideMenu() {
menuTimeout = setTimeout( function() {
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
}, 800);
}
//then add your listeners like you did - but put these functions as a handlers - like this:
menu.addEventListener("mouseenter", showMenu);
...
//in addition you need also "mouseenter" and "mouseleave" events handled on .menubuttons
I have a question that's bugging me quite a bit, been working on it a while with every road leading to dissapoints, and no suitable alternatives I've found.
How do I have the following NOT pre-load images?
var oStyle = document.createElement('style');
oStyle.setAttribute('type','text/css');
var css = 'a.hovex-imageview { -moz-outline: 1px dotted red; }';
css += 'a.hovex-imageview img.hovex-imageview { display: none;position: fixed;left: 15%;right: 85%;top:15%;bottom:85%;max-width: 100%;margin: 0;border: none; }';
css += 'a.hovex-imageview:hover img.hovex-imageview { display:block;max-width:80%;max-height:80%; }';
oStyle.innerHTML = css;
document.getElementsByTagName('head')[0].appendChild(oStyle);
var aElm = document.getElementsByTagName('a');
for (i=0; i<aElm.length; i++) {
if (aElm[i].href.match(/\.(jpg|jpeg|gif|png)$/)) {
var oImg = document.createElement('img');
oImg.setAttribute('src',aElm[i].href);
oImg.setAttribute('class','hovex-imageview');
aElm[i].appendChild(oImg);
aElm[i].setAttribute('class','hovex-imageview');
}
}
Basically, it is perfect in almost every way for my purpose, though the one drawback is it often finds itself on pages with >1000 large images, so having it only load the full image on mouseover of the link/thumb would save people some crashed browsers, I've had people complain about that.
I can see why this could be difficult to do, as it works by creating every image on load and hiding them, then showing them on hover, with it said if I'd found/managed to write an acceptable alternative I'd of used it: this seems to be what I've got.
Great thanks to any helpful wizards in advance~
I would approach it by only setting the image src on mouse over...
See this fiddle: http://jsfiddle.net/LD8t6/
var aElm = document.getElementsByTagName('a');
for (i=0; i<aElm.length; i++) {
if (aElm[i].href.match(/\.(jpg|jpeg|gif|png)$/)) {
var oImg = document.createElement('img');
oImg.setAttribute('class','hovex-imageview');
oImg.setAttribute('src','');
aElm[i].appendChild(oImg);
aElm[i].setAttribute('class','hovex-imageview');
aElm[i].onmouseover = function() {
oImg.setAttribute('src', this.href);
}
}
}
Okay so im pretty new to html/javascript/css through some tutorials and this site it's coming along. I am attempting to display a button which i use css to overlay with an image when the button is clicked I call a javascript function to send some info to my server as well as replace the button which was clicked with a new button and image overlay. here is the code snippets responsible for this (I'm basically just toggling the visibility on the buttons back and forth):
<style type = 'text/css'>
input.btn_follow {
position: absolute;
right: 2px;
top: 2px;
background-image: url(http://icons.iconarchive.com/icons/icojam/onebit/48/star-100-icon.png); /* 16px x 16px */
}
input.btn_unfollow {
visibility: hidden;
position: absolute;
right: 2px;
top: 2px;
background-image: url(http://gologic.com/imagesOld/checkmark%20-%20small.png);
}
</style>
</head><body>
<script type='text/javascript'>
function follow(series, status) {
var xhReq = new XMLHttpRequest();
var request = "follow.php?series="+series+"&status="+status
xhReq.open("GET", request, false);
xhReq.send(null);
var response = xhReq.responseText;
var IDyes = "follow_"+series
var IDno = "unfollow_"+series
if (response == 1){
document.getElementById(IDyes).style.visibility='hidden'
document.getElementById(IDno).style.visibility='visible'
}
else if (response == 0){
document.getElementById(IDyes).style.visibility='visible'
document.getElementById(IDno).style.visibility='hidden'
}
else if (response == -1){
alert("you must first login to use the follow request"); // now following show
}
}
</script>
So all of this kind of works, however for some element ID's they appear multiple times on the same html page. If this is the case only the first instance of the element is the visibility is changed and not for the rest. why is this if they have the same id ? how can I fix this? here is a link to see this in action on my web page to make this more clear http://ec2-54-234-192-222.compute-1.amazonaws.com/home.php (the button's in question are the stars)
any help would be greatly appreciated (also if there is a cleaner way scraping what i Have i'd be open to as already starting to resemble spaghetti!)
thanks -brendan
So as in the comments above Id's should only appear once per page! I'm blaming this on being a newb thanks to #Jeff shaver for clarrifying this
So, I've been trying to get this form to work (css and javascript), but I'm stuck on something: i have the form, and everything's basically working, excpet that i have a container for the form div: formbody, and a container for the submit, clear, etc.
The top div is set to height:auto; position:absolute; and the bottom is set to, nothing. it just had a width.
When the user clicks on the submit button, the formbody will need to resize, but i don't know how to get the new size of the form in order to set the position of the bottom div.
I just added more of the css - there's a background div that just holds the template open for the form -- I had that set to relative -- but formbody has a position absolute because the height needs to be auto in order to resize for the errors (and when i set it to auto without position:absolute, formbody shrank to 20px).
.background {
width: 0px;
height: 700px;
position: relative;
z-index:999;
}
.formbody {
background-image: url('');
background-repeat: no-repeat;
background-position: left bottom;
position:absolute;
left:0px;
top:50px;
width:700px;
height:auto;
padding-bottom:20px;
border:1px solid #d2d2d2;
}
.bottom {
width:700px;
}
Would this work? (Here's a live preview: http://jsfiddle.net/rcMGn/1/)
Basically what I'm doing here is getting the first node with class formwidth (as per your css) and using the DOM model to acquire its style properties.
The function getElementsByClass will return an array of all elements in the document with class formwidth (and I'm supposing there's going to be only one) and getting the first element from the array (which is supposed to be 1) and then styling it.
You don't need to care about the function...
To get the width just the first line of the code below...
formWidth = getElementsByClass('formbody',null,'form')[0].offsetWidth;
To get the height, use this one:
formHeight = getElementsByClass('formbody',null,'form')[0].offsetHeight;
and copy the function from the files of Dustin Diaz, the function below...
function getElementsByClass(searchClass,node,tag) {
var classElements = new Array();
if ( node == null )
node = document;
if ( tag == null )
tag = '*';
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
for (i = 0, j = 0; i < elsLen; i++) {
if ( pattern.test(els[i].className) ) {
classElements[j] = els[i];
j++;
}
}
return classElements;
}
something like this
<script>
var formbody_width= $(".formbody").width();
$(".submit").live("click", function) {
$(".formbody").css("height", formbody_width + 300 + "px");
}
</script>