Bootstrap collapsible not pushing down parallax - html

I got a website that was working earlier, and suddenly my bottom collapsible doesn't push the parallax down, it does push my <hr> down and also the footer, but not the parallax window, I'd be happy if someone could help me with this.
/*#media screen and (max-width: 667px) {
body {
overflow-x: hidden !important;
}
.container {
max-width: 100% !important;
overflow-x: hidden !important;
}
}*/
body {
text-align: center;
background-color: #222;
}
hr {
border-color: #191919;
}
.inf {
color: white;
}
#logo-pc {
background-color: rgb(255,255,255); /* Fallback color */
background-color: rgba(255,255,255, 0.4); /* Black w/opacity/see-through */
font-weight: bold;
border: 3px solid #f1f1f1;
position: absolute;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
padding: 20px;
text-align: center;
}
#logo-tablet {
padding-top: 130px;
width: 800px;
background-color: rgb(255,255,255); /* Fallback color */
background-color: rgba(255,255,255, 0.4); /* Black w/opacity/see-through */
font-weight: bold;
border: 3px solid #f1f1f1;
position: absolute;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
padding: 20px;
text-align: center;
}
#logo-smaller {
padding-top: 130px;
width: 600px;
background-color: rgb(255,255,255); /* Fallback color */
background-color: rgba(255,255,255, 0.4); /* Black w/opacity/see-through */
font-weight: bold;
border: 3px solid #f1f1f1;
position: absolute;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
padding: 20px;
text-align: center;
}
#logo-phone {
padding-top: 190px;
width: 200px;
background-color: rgb(255,255,255); /* Fallback color */
background-color: rgba(255,255,255, 0.4); /* Black w/opacity/see-through */
font-weight: bold;
border: 3px solid #f1f1f1;
position: absolute;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
padding: 20px;
text-align: center;
}
.panel {
background-image: none;
background-color: #393939;
color: white;
}
button {
width: 200px;
}
.included1 {
font-size: 18pt;
margin-top: 15px;
margin-bottom: 0;
}
.included {
font-size: 15pt;
color: rgb(255,255,255); /* Fallback color */
color: rgba(255,255,255, 0.5);
margin: 0px;
}
.included2 {
font-size: 15pt;
color: rgb(255,255,255); /* Fallback color */
color: rgba(255,255,255, 0.5);
margin: 5px;
}
.logo-animation {
animation-duration: 3s;
animation-delay: 0.5s;
}
.maten {
font-size: 12pt;
}
.ingredienser {
font-size: 10pt;
color: rgb(255,255,255); /* Fallback color */
color: rgba(255,255,255, 0.5);
}
.overlay {
background:transparent;
position:relative;
width:350px;
height:440px; /* your iframe height */
top:440px; /* your iframe height */
margin-top:-440px; /* your iframe height */
}
/* Footer CSS */
footer {
background: #333;
color: #eee;
font-size: 12px;
padding: 20px;
}
#keyframes heartbeat{
0%
{
transform: scale( .75 );
}
20%
{
transform: scale( 1 );
}
40%
{
transform: scale( .75 );
}
60%
{
transform: scale( 1 );
}
80%
{
transform: scale( .75 );
}
100%
{
transform: scale( .75 );
}
}
.heart{
animation: heartbeat 1s infinite;
}
/* END Footer CSS; */
<!DOCTYPE html>
<html>
<head>
<!-- Bootstrap CDN v3.3.7 -->
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="js/parallax.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- //.Bootstrap -->
</head>
<body>
<div class="col-xs-12 col-sm-6 fadeIn wow">
<div class="accordion" id="myAccordion4">
<div class="panel">
<button type="button" class="btn btn-info" data-toggle="collapse" data-target="#collapsible-13" data-parent="#myAccordion4" style="margin: 10px;">Barnmeny</button>
<div id="collapsible-13" class="collapse">
<h1 class="included">(Korv Meny)</h1>
<hr>
<p class="maten">Smal<br></p>
<h5 class="ingredienser">(Bröd 15:-) (Mos/Pommes 35:-)</h5>
<hr>
<p class="maten">Tjock<br></p>
<h5 class="ingredienser">(Bröd 25:-) (Mos/Pommes 45:-)</h5>
<hr>
<p class="maten">Pommes<br></p>
<h5 class="ingredienser">(Mellan 25:-) (Stor 35:-)</h5>
<hr>
<p class="maten">Fiskpinnar (4st 35:-) (8st 50:-)<br></p>
<h5 class="ingredienser">med potatismos & lingon</h5>
<hr>
<p class="maten">Chicky Bits (5st 40:-) (10st 60:-)<br></p>
<h5 class="ingredienser">med potatismos & lingon</h5>
<hr>
<p class="maten">Köttbullar (8st 45:-) (20st 60:-)<br></p>
<h5 class="ingredienser">med potatismos & lingon</h5>
</div>
</div>
<div class="panel">
<button type="button" class="btn btn-info" data-toggle="collapse" data-target="#collapsible-14" data-parent="#myAccordion4" style="margin: 10px;">Salladsmeny 75:-</button>
<div id="collapsible-14" class="collapse">
<h1 class="included1">I alla salladsmenyer ingår</h1>
<h1 class="included">(sallad, tomat, gurka, bröd och dressing)
<hr>
</h1>
<p class="maten">Gyros<br></p>
<h5 class="ingredienser">gyros, lök, sås</h5>
<hr>
<p class="maten">Kycklingsallad<br></p>
<h5 class="ingredienser">kyckling, ananas, salladsost, sås</h5>
<hr>
<p class="maten">Kebabsallad<br></p>
<h5 class="ingredienser">kebabkött, lök, sås</h5>
<hr>
<p class="maten">Grekisk sallad<br></p>
<h5 class="ingredienser">salladsost, oliver, paprika, lök, sås</h5>
<hr>
<p class="maten">Tonfisksallad<br></p>
<h5 class="ingredienser">tonfisk, lök, oliver, sås</h5>
<hr>
<p class="maten">Amerikansk sallad<br></p>
<h5 class="ingredienser">ost, skinka, ananas, sås</h5>
<hr>
<p class="maten">Räksallad 80:-<br></p>
<h5 class="ingredienser">räkor, ost, paprika, citron</h5>
<hr>
</div>
</div>
<div class="panel">
<button type="button" class="btn btn-info" data-toggle="collapse" data-target="#collapsible-15" data-parent="#myAccordion4" style="margin: 10px;">Pasta 75:-</button>
<div id="collapsible-15" class="collapse">
<p class="maten">Kyckling Pasta<br></p>
<h5 class="ingredienser">med curry, isbergssallad, tomat, gurka</h5>
<hr>
<p class="maten">Tonfisk Pasta<br></p>
<h5 class="ingredienser">isbergssallad, tomat, lök, dressing</h5>
<hr>
<p class="maten">Gyros Pasta<br></p>
<h5 class="ingredienser">isbergssallad, tomat, gurka, lök, dressing</h5>
<hr>
<p class="maten">Pasta Bolognese<br></p>
<h5 class="ingredienser">köttfärs, lök, sallad, tomat, gurka</h5>
</div>
</div>
</div>
</div><!-- //.col-xs-12 col-sm-6 -->
</div><!-- //.row -->
</div><!-- //.container -->
<hr>
<div style="width: 100%; height: 80vh;" class="parallax-window" data-parallax="scroll" data-image-src="images/visning5.jpg"></div>
<footer>
<div class="container">
<div class="row">
<div class="col-sm-2">
<h6>©2018 Bence Papp. All rights reserved.</h6>
</div><!-- //.col-sm-2 -->
<div class="col-sm-3">
<h5>Kontakta oss</h5>
<p>Tel. nrtel.</p>
<!-- <p>Mailaddress: exempel#domän.se</p> -->
<a target="_blank" href="https://www.google.se/maps/place/Kristianstadsv%C3%A4gen+703,+295+38+Brom%C3%B6lla/#56.0654977,14.4700173,18z/data=!3m1!4b1!4m5!3m4!1s0x46541fff4df0e3c7:0xdc6c817862e77450!8m2!3d56.0654977!4d14.4711116"><p style="color: white;"><b>Adress</b>Address<br>Address</p></a>
</div><!-- //.col-sm-3 -->
<div class="col-sm-2">
<h5>Hoppa till</h5>
<ul class="unstyled">
<li>Start</li>
</ul>
</div><!-- //.col-sm-2 -->
<div class="col-sm-2">
<!-- <h5>Sociala medier</h5>
<ul>
<li>Facebook</li>
</ul> -->
</div><!-- //.col-sm-2 -->
<div class="col-md-3">
<h6>Coded with <span class="glyphicon glyphicon-heart heart"></span> by: <span>Bence</span></h6>
</div><!-- //.col-sm-3 -->
</div><!-- //.row -->
</div><!-- //.container -->
</footer>
</body>
</html>
It does push the footer down, and the <hr> like I said, but not the parallax window...
[EDIT(adding js code)]
/*!
* parallax.js v1.5.0 (http://pixelcog.github.io/parallax.js/)
* #copyright 2016 PixelCog, Inc.
* #license MIT (https://github.com/pixelcog/parallax.js/blob/master/LICENSE)
*/
;(function ( $, window, document, undefined ) {
// Polyfill for requestAnimationFrame
// via: https://gist.github.com/paulirish/1579671
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
// Parallax Constructor
function Parallax(element, options) {
var self = this;
if (typeof options == 'object') {
delete options.refresh;
delete options.render;
$.extend(this, options);
}
this.$element = $(element);
if (!this.imageSrc && this.$element.is('img')) {
this.imageSrc = this.$element.attr('src');
}
var positions = (this.position + '').toLowerCase().match(/\S+/g) || [];
if (positions.length < 1) {
positions.push('center');
}
if (positions.length == 1) {
positions.push(positions[0]);
}
if (positions[0] == 'top' || positions[0] == 'bottom' || positions[1] == 'left' || positions[1] == 'right') {
positions = [positions[1], positions[0]];
}
if (this.positionX !== undefined) positions[0] = this.positionX.toLowerCase();
if (this.positionY !== undefined) positions[1] = this.positionY.toLowerCase();
self.positionX = positions[0];
self.positionY = positions[1];
if (this.positionX != 'left' && this.positionX != 'right') {
if (isNaN(parseInt(this.positionX))) {
this.positionX = 'center';
} else {
this.positionX = parseInt(this.positionX);
}
}
if (this.positionY != 'top' && this.positionY != 'bottom') {
if (isNaN(parseInt(this.positionY))) {
this.positionY = 'center';
} else {
this.positionY = parseInt(this.positionY);
}
}
this.position =
this.positionX + (isNaN(this.positionX)? '' : 'px') + ' ' +
this.positionY + (isNaN(this.positionY)? '' : 'px');
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
if (this.imageSrc && this.iosFix && !this.$element.is('img')) {
this.$element.css({
backgroundImage: 'url(' + this.imageSrc + ')',
backgroundSize: 'cover',
backgroundPosition: this.position
});
}
return this;
}
if (navigator.userAgent.match(/(Android)/)) {
if (this.imageSrc && this.androidFix && !this.$element.is('img')) {
this.$element.css({
backgroundImage: 'url(' + this.imageSrc + ')',
backgroundSize: 'cover',
backgroundPosition: this.position
});
}
return this;
}
this.$mirror = $('<div />').prependTo(this.mirrorContainer);
var slider = this.$element.find('>.parallax-slider');
var sliderExisted = false;
if (slider.length == 0)
this.$slider = $('<img />').prependTo(this.$mirror);
else {
this.$slider = slider.prependTo(this.$mirror)
sliderExisted = true;
}
this.$mirror.addClass('parallax-mirror').css({
visibility: 'hidden',
zIndex: this.zIndex,
position: 'fixed',
top: 0,
left: 0,
overflow: 'hidden'
});
this.$slider.addClass('parallax-slider').one('load', function() {
if (!self.naturalHeight || !self.naturalWidth) {
self.naturalHeight = this.naturalHeight || this.height || 1;
self.naturalWidth = this.naturalWidth || this.width || 1;
}
self.aspectRatio = self.naturalWidth / self.naturalHeight;
Parallax.isSetup || Parallax.setup();
Parallax.sliders.push(self);
Parallax.isFresh = false;
Parallax.requestRender();
});
if (!sliderExisted)
this.$slider[0].src = this.imageSrc;
if (this.naturalHeight && this.naturalWidth || this.$slider[0].complete || slider.length > 0) {
this.$slider.trigger('load');
}
}
// Parallax Instance Methods
$.extend(Parallax.prototype, {
speed: 0.2,
bleed: 0,
zIndex: -100,
iosFix: true,
androidFix: true,
position: 'center',
overScrollFix: false,
mirrorContainer: 'body',
refresh: function() {
this.boxWidth = this.$element.outerWidth();
this.boxHeight = this.$element.outerHeight() + this.bleed * 2;
this.boxOffsetTop = this.$element.offset().top - this.bleed;
this.boxOffsetLeft = this.$element.offset().left;
this.boxOffsetBottom = this.boxOffsetTop + this.boxHeight;
var winHeight = Parallax.winHeight;
var docHeight = Parallax.docHeight;
var maxOffset = Math.min(this.boxOffsetTop, docHeight - winHeight);
var minOffset = Math.max(this.boxOffsetTop + this.boxHeight - winHeight, 0);
var imageHeightMin = this.boxHeight + (maxOffset - minOffset) * (1 - this.speed) | 0;
var imageOffsetMin = (this.boxOffsetTop - maxOffset) * (1 - this.speed) | 0;
var margin;
if (imageHeightMin * this.aspectRatio >= this.boxWidth) {
this.imageWidth = imageHeightMin * this.aspectRatio | 0;
this.imageHeight = imageHeightMin;
this.offsetBaseTop = imageOffsetMin;
margin = this.imageWidth - this.boxWidth;
if (this.positionX == 'left') {
this.offsetLeft = 0;
} else if (this.positionX == 'right') {
this.offsetLeft = - margin;
} else if (!isNaN(this.positionX)) {
this.offsetLeft = Math.max(this.positionX, - margin);
} else {
this.offsetLeft = - margin / 2 | 0;
}
} else {
this.imageWidth = this.boxWidth;
this.imageHeight = this.boxWidth / this.aspectRatio | 0;
this.offsetLeft = 0;
margin = this.imageHeight - imageHeightMin;
if (this.positionY == 'top') {
this.offsetBaseTop = imageOffsetMin;
} else if (this.positionY == 'bottom') {
this.offsetBaseTop = imageOffsetMin - margin;
} else if (!isNaN(this.positionY)) {
this.offsetBaseTop = imageOffsetMin + Math.max(this.positionY, - margin);
} else {
this.offsetBaseTop = imageOffsetMin - margin / 2 | 0;
}
}
},
render: function() {
var scrollTop = Parallax.scrollTop;
var scrollLeft = Parallax.scrollLeft;
var overScroll = this.overScrollFix ? Parallax.overScroll : 0;
var scrollBottom = scrollTop + Parallax.winHeight;
if (this.boxOffsetBottom > scrollTop && this.boxOffsetTop <= scrollBottom) {
this.visibility = 'visible';
this.mirrorTop = this.boxOffsetTop - scrollTop;
this.mirrorLeft = this.boxOffsetLeft - scrollLeft;
this.offsetTop = this.offsetBaseTop - this.mirrorTop * (1 - this.speed);
} else {
this.visibility = 'hidden';
}
this.$mirror.css({
transform: 'translate3d('+this.mirrorLeft+'px, '+(this.mirrorTop - overScroll)+'px, 0px)',
visibility: this.visibility,
height: this.boxHeight,
width: this.boxWidth
});
this.$slider.css({
transform: 'translate3d('+this.offsetLeft+'px, '+this.offsetTop+'px, 0px)',
position: 'absolute',
height: this.imageHeight,
width: this.imageWidth,
maxWidth: 'none'
});
}
});
// Parallax Static Methods
$.extend(Parallax, {
scrollTop: 0,
scrollLeft: 0,
winHeight: 0,
winWidth: 0,
docHeight: 1 << 30,
docWidth: 1 << 30,
sliders: [],
isReady: false,
isFresh: false,
isBusy: false,
setup: function() {
if (this.isReady) return;
var self = this;
var $doc = $(document), $win = $(window);
var loadDimensions = function() {
Parallax.winHeight = $win.height();
Parallax.winWidth = $win.width();
Parallax.docHeight = $doc.height();
Parallax.docWidth = $doc.width();
};
var loadScrollPosition = function() {
var winScrollTop = $win.scrollTop();
var scrollTopMax = Parallax.docHeight - Parallax.winHeight;
var scrollLeftMax = Parallax.docWidth - Parallax.winWidth;
Parallax.scrollTop = Math.max(0, Math.min(scrollTopMax, winScrollTop));
Parallax.scrollLeft = Math.max(0, Math.min(scrollLeftMax, $win.scrollLeft()));
Parallax.overScroll = Math.max(winScrollTop - scrollTopMax, Math.min(winScrollTop, 0));
};
$win.on('resize.px.parallax load.px.parallax', function() {
loadDimensions();
self.refresh();
Parallax.isFresh = false;
Parallax.requestRender();
})
.on('scroll.px.parallax load.px.parallax', function() {
loadScrollPosition();
Parallax.requestRender();
});
loadDimensions();
loadScrollPosition();
this.isReady = true;
var lastPosition = -1;
function frameLoop() {
if (lastPosition == window.pageYOffset) { // Avoid overcalculations
window.requestAnimationFrame(frameLoop);
return false;
} else lastPosition = window.pageYOffset;
self.render();
window.requestAnimationFrame(frameLoop);
}
frameLoop();
},
configure: function(options) {
if (typeof options == 'object') {
delete options.refresh;
delete options.render;
$.extend(this.prototype, options);
}
},
refresh: function() {
$.each(this.sliders, function(){ this.refresh(); });
this.isFresh = true;
},
render: function() {
this.isFresh || this.refresh();
$.each(this.sliders, function(){ this.render(); });
},
requestRender: function() {
var self = this;
self.render();
self.isBusy = false;
},
destroy: function(el){
var i,
parallaxElement = $(el).data('px.parallax');
parallaxElement.$mirror.remove();
for(i=0; i < this.sliders.length; i+=1){
if(this.sliders[i] == parallaxElement){
this.sliders.splice(i, 1);
}
}
$(el).data('px.parallax', false);
if(this.sliders.length === 0){
$(window).off('scroll.px.parallax resize.px.parallax load.px.parallax');
this.isReady = false;
Parallax.isSetup = false;
}
}
});
// Parallax Plugin Definition
function Plugin(option) {
return this.each(function () {
var $this = $(this);
var options = typeof option == 'object' && option;
if (this == window || this == document || $this.is('body')) {
Parallax.configure(options);
}
else if (!$this.data('px.parallax')) {
options = $.extend({}, $this.data(), options);
$this.data('px.parallax', new Parallax(this, options));
}
else if (typeof option == 'object')
{
$.extend($this.data('px.parallax'), options);
}
if (typeof option == 'string') {
if(option == 'destroy'){
Parallax.destroy(this);
}else{
Parallax[option]();
}
}
});
}
var old = $.fn.parallax;
$.fn.parallax = Plugin;
$.fn.parallax.Constructor = Parallax;
// Parallax No Conflict
$.fn.parallax.noConflict = function () {
$.fn.parallax = old;
return this;
};
// Parallax Data-API
$( function () {
$('[data-parallax="scroll"]').parallax();
});
}(jQuery, window, document));
[EDIT]
I'm using this parallax.js
Thanks in advance!

First of all, you should validate your HTML, </head> and <body> tags are missing and there are two </div> moreover. (line 96 or something like that)
For your purpose, I think is parallax.js is overkill. You can just use background: url('./image.jgp') 50% 50%; background-size: cover; background-attachment: fixed;.
Your content has 80vh and when it overflows it (panels do that), then it will fuck up your footer and other content. Maybe you could use position: absolute;.

Related

How to achive this timeline look only using html and css maybe some bootstrap

So far i only can achieve to this point. I am using example code from w3school
I trying to figure out to get year in the same position like in the picture but no luck and also tried to insert image background for circle but only manage to color fill
/* The actual timeline (the vertical ruler) */
.timeline {
position: relative;
max-width: 1200px;
margin: 0 auto;
}
/* The actual timeline (the vertical ruler) */
.timeline::after {
content: ' ';
position: absolute;
width: 6px;
background-color: #ffc200;
top: 0;
bottom: 0;
left: 50%;
margin-left: -5px;
}
/* Container around content */
.contaiment {
padding: 10px 40px;
position: relative;
background-color: inherit;
width: 50%;
}
/* The circles on the timeline */
.contaiment::after {
content: '';
position: absolute;
width: 25px;
height: 25px;
right: -11px;
background-color: #ffc200;
border: 4px solid #ffc200;
top: 15px;
border-radius: 50%;
z-index: 1;
}
/* Place the container to the left */
.left {
left: 0;
}
/* Place the container to the right */
.right {
left: 50%;
}
/* Add arrows to the left container (pointing right) */
/* .left::before {
content: " ";
height: 0;
position: absolute;
top: 22px;
width: 0;
z-index: 1;
right: 30px;
border: medium solid white;
border-width: 10px 0 10px 10px;
border-color: transparent transparent transparent white;
} */
/* Add arrows to the right container (pointing left) */
.right::before {
content: " ";
height: 0;
position: absolute;
top: 22px;
width: 0;
z-index: 1;
left: 30px;
border: medium solid white;
border-width: 10px 10px 10px 0;
border-color: transparent white transparent transparent;
}
/* Fix the circle for containers on the right side */
.right::after {
left: -16px;
}
/* The actual content */
.content {
padding: 20px 30px;
background-color: white;
position: relative;
border-radius: 6px;
}
<div class="timeline">
<div class="contaiment left">
<div class="content">
<p>2017
<p>
<p>Lorem ipsum..</p>
</div>
</div>
<div class="contaiment right">
<div class="content">
<p class="">2017
<p>
<p>Lorem ipsum..</p>
</div>
</div>
<div class="contaiment left">
<div class="content">
<p class="">2017
<p>
<p>Lorem ipsum..</p>
</div>
</div>
</div>
I know you want to do something with only HTML and CSS, but take a look at this JQuery library. It's very easy to use.
https://github.com/musclesoft/jquery-connections/wiki/API
You just need to set the 2 elements you want to connect.
$("#div1, #div2").connections();
$("#div2, #div3").connections();
$("#div3, #div4").connections();
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
<title>Hello, world!</title>
</head>
<body>
<p style="width: 50px;height: 50px;border: 1px solid black;" id="div1">TEST</p>
<p style="width: 50px;height: 50px;margin-left: 50px;border: 1px solid black;" id="div2">TEST</p>
<p style="width: 50px;height: 50px;margin-left: 200px;border: 1px solid black;" id="div3">TEST</p>
<p style="width: 50px;height: 50px;margin-left: 300px;border: 1px solid black;" id="div4">TEST</p>
<!-- Script retrieved from https://raw.githubusercontent.com/musclesoft/jquery-connections/gh-pages/jquery.connections.js -->
<script>
(function($) {
$.fn.connections = function(options) {
if (options === "update") {
return processConnections(update, this);
} else if (options === "remove") {
return processConnections(destroy, this);
} else {
options = $.extend(
true,
{
borderClasses: {},
class: "connection",
css: {},
from: this,
tag: "connection",
to: this,
within: ":root"
},
options
);
connect(options);
return this;
}
};
$.event.special.connections = {
teardown: function(namespaces) {
processConnections(destroy, $(this));
}
};
var connect = function(options) {
var borderClasses = options.borderClasses;
var tag = options.tag;
var end1 = $(options.from);
var end2 = $(options.to);
var within = $(options.within);
delete options.borderClasses;
delete options.tag;
delete options.from;
delete options.to;
delete options.within;
within.each(function() {
var container = this;
var done = new Array();
end1.each(function() {
var node = this;
done.push(this);
end2.not(done).each(function() {
createConnection(
container,
[node, this],
tag,
borderClasses,
options
);
});
});
});
};
var createConnection = function(
container,
nodes,
tag,
borderClasses,
options
) {
var css = $.extend({ position: "absolute" }, options.css);
var connection = $("<" + tag + "/>", options).css(css);
connection.appendTo(container);
var border_w = (connection.outerWidth() - connection.innerWidth()) / 2;
var border_h = (connection.outerHeight() - connection.innerHeight()) / 2;
if (border_w <= 0 && border_h <= 0) {
border_w = border_h = 1;
}
var data = {
borderClasses: borderClasses,
border_h: border_h,
border_w: border_w,
node_from: $(nodes[0]),
node_to: $(nodes[1]),
nodes_dom: nodes,
css: css
};
if ("none" === connection.css("border-top-style")) {
data.css.borderStyle = "solid";
}
$.data(connection.get(0), "connection", data);
$.data(connection.get(0), "connections", [connection.get(0)]);
for (var i = 0; i < 2; i++) {
var connections = connection.add($.data(nodes[i], "connections")).get();
$.data(nodes[i], "connections", connections);
if (connections.length == 1) {
$(nodes[i]).on("connections.connections", false);
}
}
update(connection.get(0));
};
var destroy = function(connection) {
var nodes = $.data(connection, "connection").nodes_dom;
for (var i = 0; i < 2; i++) {
var connections = $($.data(nodes[i], "connections"))
.not(connection)
.get();
$.data(nodes[i], "connections", connections);
}
$(connection).remove();
};
var getState = function(data) {
data.rect_from = data.nodes_dom[0].getBoundingClientRect();
data.rect_to = data.nodes_dom[1].getBoundingClientRect();
var cached = data.cache;
data.cache = [
data.rect_from.top,
data.rect_from.right,
data.rect_from.bottom,
data.rect_from.left,
data.rect_to.top,
data.rect_to.right,
data.rect_to.bottom,
data.rect_to.left
];
data.hidden =
0 === (data.cache[0] | data.cache[1] | data.cache[2] | data.cache[3]) ||
0 === (data.cache[4] | data.cache[5] | data.cache[6] | data.cache[7]);
data.unmodified = true;
if (cached === undefined) {
return (data.unmodified = false);
}
for (var i = 0; i < 8; i++) {
if (cached[i] !== data.cache[i]) {
return (data.unmodified = false);
}
}
};
var update = function(connection) {
var data = $.data(connection, "connection");
getState(data);
if (data.unmodified) {
return;
}
var border_h = data.border_h;
var border_w = data.border_w;
var from = data.rect_from;
var to = data.rect_to;
var b = (from.bottom + from.top) / 2;
var r = (to.left + to.right) / 2;
var t = (to.bottom + to.top) / 2;
var l = (from.left + from.right) / 2;
var h = ["right", "left"];
if (l > r) {
h = ["left", "right"];
var x = Math.max(r - border_w / 2, Math.min(from.right, to.right));
r = l + border_w / 2;
l = x;
} else {
l -= border_w / 2;
r = Math.min(r + border_w / 2, Math.max(from.left, to.left));
}
var v = ["bottom", "top"];
if (t > b) {
v = ["top", "bottom"];
var x = Math.max(b - border_h / 2, Math.min(from.bottom, to.bottom));
b = t + border_h / 2;
t = x;
} else {
b = Math.min(b, Math.max(from.top, to.top));
t -= border_h / 2;
}
var width = r - l;
var height = b - t;
if (width < border_w) {
t = Math.max(t, Math.min(from.bottom, to.bottom));
b = Math.min(b, Math.max(from.top, to.top));
l = Math.max(from.left, to.left);
r = Math.min(from.right, to.right);
r = l = (l + r - border_w) / 2;
}
if (height < border_h) {
l = Math.max(l, Math.min(from.right, to.right));
r = Math.min(r, Math.max(from.left, to.left));
t = Math.max(from.top, to.top);
b = Math.min(from.bottom, to.bottom);
b = t = (t + b - border_h) / 2;
}
width = r - l;
height = b - t;
width <= 0 && (border_h = 0);
height <= 0 && (border_w = 0);
var style =
"border-" +
v[0] +
"-" +
h[0] +
"-radius: 0;" +
"border-" +
v[0] +
"-" +
h[1] +
"-radius: 0;" +
"border-" +
v[1] +
"-" +
h[0] +
"-radius: 0;";
(border_h <= 0 || border_w <= 0) &&
(style += "border-" + v[1] + "-" + h[1] + "-radius: 0;");
if (data.hidden) {
style += "display: none;";
} else {
data.css["border-" + v[0] + "-width"] = 0;
data.css["border-" + h[0] + "-width"] = 0;
data.css["border-" + v[1] + "-width"] = border_h;
data.css["border-" + h[1] + "-width"] = border_w;
var current_rect = connection.getBoundingClientRect();
data.css.left = connection.offsetLeft + l - current_rect.left;
data.css.top = connection.offsetTop + t - current_rect.top;
data.css.width = width - border_w;
data.css.height = height - border_h;
}
var bc = data.borderClasses;
$(connection)
.removeClass(bc[v[0]])
.removeClass(bc[h[0]])
.addClass(bc[v[1]])
.addClass(bc[h[1]])
.attr("style", style)
.css(data.css);
};
var processConnections = function(method, elements) {
return elements.each(function() {
var connections = $.data(this, "connections");
if (connections instanceof Array) {
for (var i = 0, len = connections.length; i < len; i++) {
method(connections[i]);
}
}
});
};
})(jQuery);
</script>
<script defer type="text/javascript">
$("#div1, #div2").connections();
$("#div2, #div3").connections();
$("#div3, #div4").connections();
</script>
</body>
</html>

Circular window

I have this fiddle.
https://jsfiddle.net/oeuc8L9y/2/
If you click the background the coordinates where you clicked get centered.
Is it possible to make the pointer events only work inside the hole?
Since I'm using an image for the ring the whole thing blocks the pointer-events but if I set it to pointer-events: none; I can click trough the ring too.
This is good.
This is bad.
I assume a way would be to get the pixel coordinate and calculate if it's inside the hole but I feel like that would only work for a specific screen size and it'd break if resized.
tooltip_X = $("#x-coords");
tooltip_Y = $("#y-coords");
$("#background").on("mouseover", (e) => {
showTooltip(e);
});
$("#background").on("mousemove", (e) => {
updateCoords(e);
moveTooltip(e);
});
$("#background").on("mouseout", (e) => {
hideTooltip(e);
});
$("#background").on("click", (e) => {
move(e);
updateCoords(e);
});
function showTooltip(e) {
$("#tooltip").css("display", "block");
}
function hideTooltip(e) {
$("#tooltip").css("display", "none");
}
function moveTooltip(e) {
var left = 0;
var top = 0;
if (e.pageX + $("#tooltip").width() + 10 < document.body.clientWidth) {
left = e.pageX + 10;
} else {
left = document.body.clientWidth + 5 - $("#tooltip").width();
}
if (e.pageY + $("#tooltip").height() + 10 < document.body.clientHeight) {
top = e.pageY + 10;
} else {
top = document.body.clientHeight + 5 - $("#tooltip").height();
}
$("#tooltip").offset({ top: top, left: left });
}
function updateCoords(e) {
var mouse_x = e.clientX - $("#background").offset().left;
var mouse_y = e.clientY - $("#background").offset().top;
$("#x-coords").text(Number.parseInt(mouse_x));
$("#y-coords").text(Number.parseInt(mouse_y));
}
function move(e) {
var mouse_x = e.clientX - $("#background").offset().left;
var mouse_y = e.clientY - $("#background").offset().top;
var new_x = 0;
var new_y = 0;
if (mouse_x < 250) {
mouse_x = 250;
}
if (mouse_y < 250) {
mouse_y = 250;
}
if (mouse_x > 1670) {
mouse_x = 1670;
}
if (mouse_y > 950) {
mouse_y = 950;
}
new_x = -(mouse_x - 250);
new_y = -(mouse_y - 250);
$("#background").css("margin-top", new_y);
$("#background").css("margin-left", new_x);
}
#container {
position: relative;
width: 500px;
height: 500px;
overflow: hidden;
z-index: 100;
}
#ring {
position: absolute;
border-radius: 100%;
max-height: 500px;
max-width: 500px;
z-index: 90;
pointer-events: none;
}
#lens {
position: absolute;
border: 1px solid red;
/*
something to make it round
*/
z-index: 80;
pointer-events: none;
}
#background {
position: absolute;
width: 1920px;
height: 1200px;
background-image: url("https://wallpaperaccess.com/full/197542.jpg");
z-index: 80;
outline: 5px dotted purple;
outline-offset: -5px;
}
#tooltip {
position: absolute;
white-space: nowrap;
background: #ffffcc;
border: 1px solid black;
padding: 5px;
color: black;
z-index: 999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head> </head>
<body>
<div id="container">
<img id="ring" src="https://pngimg.com/uploads/circle/circle_PNG26.png" alt="Lens Ring" />
<div id="lens"></div>
<div id="background"></div>
<span id="tooltip"> Div Coords (<span id="x-coords"></span>,<span id="y-coords"></span>) </span>
</div>
</body>
</html>

CSS: Make children inside parent responsive

So I am not using any CSS framework like bootstrap to get responsiveness out of the box which is why I am having trouble making responsive layout.
Please see jsbin
I essentially what to auto-resize colorful boxes based on browser window size eg that should shrink or grow automatically based on window size. Colorful boxes inside their parent should always be in horizontal row but should be able to adjust their width and height like this example.
I tried using flex-wrap: nowrap; but it didn't do the trick :(
Please note that colorful boxes are using position:absolute with parent's position being relative. I am also adding left css property to these boxes via JavaScript to change their position for the sake of sliding animation.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
let width = options.width || 200;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
width: auto;
height: auto;
margin: 25px;
display: flex;
align-items: center;
justify-content: center;
}
.carousel {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.carousel .carousel-item {
position: absolute;
transition: transform .5s ease-in-out;
color: #fff;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
padding-top: 150px;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
margin-top: 150px;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
top: 104px;
box-shadow: 0 1px 0 #999;
}
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
Problem here was:
Width was hard-coded in your JS, so if width is in px it can't be responsive.
By applying position:absolute to you carousel-item, it forced the children to get out of the box.
What I did:
Got rid of the static width and other functionalities related to width from your JS
Removed position:absolute from carousel-item
Let me know if this is what you are expecting.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
// let width = options.width || 100;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
// slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
// slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
// elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
// slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
height: auto;
margin: 25px;
display: flex;
}
.carousel {
flex: 1;
height: 100%;
width: 100vh;
/* overflow:hidden; */
display: flex;
align-items: center;
justify-content: center;
}
.carousel .carousel-item {
transition: transform .5s ease-in-out;
color: #fff;
flex: 1;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
box-shadow: 0 1px 0 #999;
}
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>

Why does HTML <a> tag sometimes work and other times doesn't in a 2 level nested accordion?

I'm using Bootstrap and I'm trying to make an accordion of items nested inside an accordion. So it would be like top level is an accordion containing many physical quantities panels, and each physical quantity panel's content is an accordion containing a list of SI-conversion-factors panels whose content is a list of units of measurements.
Every a tag on the top level accordion works but in the 2nd level accordion some a tags work and some don't.
Download:
You can see for your self by downloading a zip containing minimum amount of code to replicate the problem:
minimal-code.html
collapse.js
jquery-2.2.4.min.js
css.css
Pictures:
I took some pictures before and after clicking one that don't work and one that do work. Pictures are not from the minimal code to replicate problem version.
I like the accordion style to present the data, what would I have to do to make it work? If not, I can try other styles such as collapse or a simple nested list.
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset='utf-8'>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- <script src="jquery-2.2.4.min.js"></script>
<script src="collapse.js"></script>
<link rel="stylesheet" href="css.css"> -->
</head>
<body>
<div aria-multiselectable="true" id="dimension-accordion" role="tablist" data-reactid=".0.3.0.0.1.0">
<div class="panel panel-info" data-reactid=".0.3.0.0.1.0.$79">
<div role="tab" id="heading-dimension-id-79" class="panel-heading" data-reactid=".0.3.0.0.1.0.$79.0">
<a data-toggle="collapse" data-parent="#dimension-accordion" href="#collapse-dimension-id-79" aria-expanded="false" aria-controls="collapse-dimension-id-79" data-reactid=".0.3.0.0.1.0.$79.0.0">
<h2 data-reactid=".0.3.0.0.1.0.$79.0.0.0"><span class="badge" data-reactid=".0.3.0.0.1.0.$79.0.0.0.0">31</span><span data-reactid=".0.3.0.0.1.0.$79.0.0.0.1">current = {"A" 1}</span></h2>
</a>
</div>
<div id="collapse-dimension-id-79" aria-labelledby="heading-dimension-id-79" role="tabpanel" class="panel-collapse collapse" data-reactid=".0.3.0.0.1.0.$79.1">
<div id="accordion-conversion-factor-for-79" role="tablist" aria-multiselectable="true" data-reactid=".0.3.0.0.1.0.$79.1.0">
<div class="panel panel-default" data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835">
<div role="tab" id="0.999835" class="panel-heading" data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.0">
<a data-toggle="collapse" data-parent="#accordion-conversion-factor-for-79" href="#collapse-id-0.999835" aria-expanded="false" aria-controls="collapse-id-0.999835" data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.0.0">
<h3 data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.0.0.0"><span class="badge" data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.0.0.0.0">4</span><span data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.0.0.0.1">0.999835</span></h3>
</a>
</div>
<div id="collapse-id-0.999835" aria-labelledby="0.999835" role="tabpanel" class="panel-collapse collapse" data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.1">
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.1.$4058">intamp</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.1.$3015">intampere</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.1.$4207">intamperes</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$0=1999835.1.$4175">intamps</h4>
</div>
</div>
<div class="panel panel-default" data-reactid=".0.3.0.0.1.0.$79.1.0.$1">
<div role="tab" id="1" class="panel-heading" data-reactid=".0.3.0.0.1.0.$79.1.0.$1.0">
<a data-toggle="collapse" data-parent="#accordion-conversion-factor-for-79" href="#collapse-id-1" aria-expanded="false" aria-controls="collapse-id-1" data-reactid=".0.3.0.0.1.0.$79.1.0.$1.0.0">
<h3 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.0.0.0"><span class="badge" data-reactid=".0.3.0.0.1.0.$79.1.0.$1.0.0.0.0">7</span><span data-reactid=".0.3.0.0.1.0.$79.1.0.$1.0.0.0.1">1</span></h3>
</a>
</div>
<div id="collapse-id-1" aria-labelledby="1" role="tabpanel" class="panel-collapse collapse" data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1">
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1.$3269">A</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1.$2566">amp</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1.$1900">ampere</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1.$1857">amperes</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1.$303">amps</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1.$4186">galvat</h4>
<h4 data-reactid=".0.3.0.0.1.0.$79.1.0.$1.1.$604">galvats</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
CSS:
.panel {
margin-bottom: 20px;
background-color: #fff;
border: 1px solid transparent;
border-radius: 4px;
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
}
.panel-info {
border-color: #bce8f1;
}
.panel-info > .panel-heading {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
.panel-info > .panel-heading + .panel-collapse > .panel-body {
border-top-color: #bce8f1;
}
.panel-info > .panel-heading .badge {
color: #d9edf7;
background-color: #31708f;
}
.panel-info > .panel-footer + .panel-collapse > .panel-body {
border-bottom-color: #bce8f1;
}
.panel-heading {
padding: 10px 15px;
border-bottom: 1px solid transparent;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
.badge {
display: inline-block;
min-width: 10px;
padding: 3px 7px;
font-size: 12px;
font-weight: bold;
line-height: 1;
color: #fff;
text-align: center;
white-space: nowrap;
vertical-align: middle;
background-color: #777;
border-radius: 10px;
}
.collapse {
display: none;
}
.collapse.in {
display: block;
}
jquery-2.2.4.min.js:
https://code.jquery.com/jquery-2.2.4.min.js
collapse.js:
/* ========================================================================
* Bootstrap: collapse.js v3.3.6
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// COLLAPSE PUBLIC CLASS DEFINITION
// ================================
var Collapse = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, Collapse.DEFAULTS, options)
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
'[data-toggle="collapse"][data-target="#' + element.id + '"]')
this.transitioning = null
if (this.options.parent) {
this.$parent = this.getParent()
} else {
this.addAriaAndCollapsedClass(this.$element, this.$trigger)
}
if (this.options.toggle) this.toggle()
}
Collapse.VERSION = '3.3.6'
Collapse.TRANSITION_DURATION = 350
Collapse.DEFAULTS = {
toggle: true
}
Collapse.prototype.dimension = function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
Collapse.prototype.show = function () {
if (this.transitioning || this.$element.hasClass('in')) return
var activesData
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
if (actives && actives.length) {
activesData = actives.data('bs.collapse')
if (activesData && activesData.transitioning) return
}
var startEvent = $.Event('show.bs.collapse')
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
if (actives && actives.length) {
Plugin.call(actives, 'hide')
activesData || actives.data('bs.collapse', null)
}
var dimension = this.dimension()
this.$element
.removeClass('collapse')
.addClass('collapsing')[dimension](0)
.attr('aria-expanded', true)
this.$trigger
.removeClass('collapsed')
.attr('aria-expanded', true)
this.transitioning = 1
var complete = function () {
this.$element
.removeClass('collapsing')
.addClass('collapse in')[dimension]('')
this.transitioning = 0
this.$element
.trigger('shown.bs.collapse')
}
if (!$.support.transition) return complete.call(this)
var scrollSize = $.camelCase(['scroll', dimension].join('-'))
this.$element
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
}
Collapse.prototype.hide = function () {
if (this.transitioning || !this.$element.hasClass('in')) return
var startEvent = $.Event('hide.bs.collapse')
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
var dimension = this.dimension()
this.$element[dimension](this.$element[dimension]())[0].offsetHeight
this.$element
.addClass('collapsing')
.removeClass('collapse in')
.attr('aria-expanded', false)
this.$trigger
.addClass('collapsed')
.attr('aria-expanded', false)
this.transitioning = 1
var complete = function () {
this.transitioning = 0
this.$element
.removeClass('collapsing')
.addClass('collapse')
.trigger('hidden.bs.collapse')
}
if (!$.support.transition) return complete.call(this)
this.$element
[dimension](0)
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)
}
Collapse.prototype.toggle = function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
Collapse.prototype.getParent = function () {
return $(this.options.parent)
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
.each($.proxy(function (i, element) {
var $element = $(element)
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
}, this))
.end()
}
Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
var isOpen = $element.hasClass('in')
$element.attr('aria-expanded', isOpen)
$trigger
.toggleClass('collapsed', !isOpen)
.attr('aria-expanded', isOpen)
}
function getTargetFromTrigger($trigger) {
var href
var target = $trigger.attr('data-target')
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
return $(target)
}
// COLLAPSE PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.collapse')
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.collapse
$.fn.collapse = Plugin
$.fn.collapse.Constructor = Collapse
// COLLAPSE NO CONFLICT
// ====================
$.fn.collapse.noConflict = function () {
$.fn.collapse = old
return this
}
// COLLAPSE DATA-API
// =================
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
var $this = $(this)
if (!$this.attr('data-target')) e.preventDefault()
var $target = getTargetFromTrigger($this)
var data = $target.data('bs.collapse')
var option = data ? 'toggle' : $this.data()
Plugin.call($target, option)
})
}(jQuery);

Auto size fixed text to fill dynamic size container

I'm trying to recreate this game in JavaScript. For this game, I need cells with numbers in them.
I want the game to size to the available space in the browser. I've managed to do this by using vw and vh in combination with width and min-width (and height) as you can see in the example. If you size the viewport in which the cells are shown, the cells size along.
The problem
And now, I want the text in it to size along too. The container (the cell) resizes, and the font of the digit should size accordingly. I now used vmax as a unit, but this doesn't take the horizontal sizing into account. And since there is no min-font-size, I cannot do the same trick I used for the cells themselves.
No jQuery please
I've tried and searched. Most notably, I found Auto-size dynamic text to fill fixed size container, however I think my question is reversed. The text is fixed, and I could set an initial font-size. I just need the font to scale along with the size of the element, so maybe this can be done through CSS after all.
Besides, most questions about this subject suggest using one of the various jQuery plugins and I'm not looking for a jQuery solution. I'm trying to make this game just for fun and practice, and I've set a goal to create it without jQuery. Actually I'm not even looking for a vanilla JavaScript solution. In the end it may boil down to that, but I haven't tried building it myself yet, so I don't want to ask for JavaScript here now. No, I'm looking for a pure CSS solution, if any.
The snippet
The dressed down snippet works best in full page mode. Nevermind the inline styling. These elements are actually generated by JavaScript and need be moved around. And sorry for the chunk of HTML. I brought it down to two cells at first, but that looked confusing, because they only filled a small part of the screen, and you couldn't see what was going on.
.game11,
.game11 .cell,
.game11 .cell .digit {
box-sizing: border-box;
}
.game11 {
width: 90vw;
height: 90vw;
max-width: 90vh;
max-height: 90vh;
box-sizing: border-box;
position: relative;
}
.game11 .cell {
width: 20%;
height: 20%;
position: absolute;
font-size: 7vmax; /* Font size. This obviously doesn't work */
}
.game11 .cell .digit {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: 3px solid #666633;
text-align: center;
padding-top: 13%;
font-family: Impact, Charcoal, sans-serif;
color: #111111;
}
<div class="game11">
<div class="cell" style="left: 0%; top: 0%;">
<div class="digit digit2" style="top: 0px;">2</div>
</div>
<div class="cell" style="left: 20%; top: 0%;">
<div class="digit digit2" style="top: 0px;">2</div>
</div>
<div class="cell" style="left: 40%; top: 0%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
<div class="cell" style="left: 60%; top: 0%;">
<div class="digit digit1" style="top: 0px;">1</div>
</div>
<div class="cell" style="left: 80%; top: 0%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
<div class="cell" style="left: 0%; top: 20%;">
<div class="digit digit1" style="top: 0px;">1</div>
</div>
<div class="cell" style="left: 20%; top: 20%;">
<div class="digit digit1" style="top: 0px;">1</div>
</div>
<div class="cell" style="left: 40%; top: 20%;">
<div class="digit digit4" style="top: 0px;">4</div>
</div>
<div class="cell" style="left: 60%; top: 20%;">
<div class="digit digit1" style="top: 0px;">1</div>
</div>
<div class="cell" style="left: 80%; top: 20%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
<div class="cell" style="left: 0%; top: 40%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
<div class="cell" style="left: 20%; top: 40%;">
<div class="digit digit2" style="top: 0px;">2</div>
</div>
<div class="cell" style="left: 40%; top: 40%;">
<div class="digit digit4" style="top: 0px;">4</div>
</div>
<div class="cell" style="left: 60%; top: 40%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
<div class="cell" style="left: 80%; top: 40%;">
<div class="digit digit4" style="top: 0px;">4</div>
</div>
<div class="cell" style="left: 0%; top: 60%;">
<div class="digit digit2" style="top: 0px;">2</div>
</div>
<div class="cell" style="left: 20%; top: 60%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
<div class="cell" style="left: 40%; top: 60%;">
<div class="digit digit5" style="top: 0px;">5</div>
</div>
<div class="cell" style="left: 60%; top: 60%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
<div class="cell" style="left: 80%; top: 60%;">
<div class="digit digit1" style="top: 0px;">1</div>
</div>
<div class="cell" style="left: 0%; top: 80%;">
<div class="digit digit4">4</div>
</div>
<div class="cell" style="left: 20%; top: 80%;">
<div class="digit digit1" style="top: 0px;">1</div>
</div>
<div class="cell" style="left: 40%; top: 80%;">
<div class="digit digit2" style="top: 0px;">2</div>
</div>
<div class="cell" style="left: 60%; top: 80%;">
<div class="digit digit5">5</div>
</div>
<div class="cell" style="left: 80%; top: 80%;">
<div class="digit digit3" style="top: 0px;">3</div>
</div>
</div>
Updated: The 'full' (still unfinished) game, including the fix suggested by Pangloss
In the snippet below, you can find the game as I have it so far. It's working for the largest part, so if it's not helpful for the question, at least it may be fun or helpful to future visitors.
/**
* Game11 class
*/
function Game11(container) {
var game = this;
game.element = container;
game.cells = [];
game.highestValue = 4;
game.animations = [];
game.animating = false;
var four = this.random(25);
for (var i = 0; i < 25; i++) {
var cell = new Cell(game, i);
var value = this.random(3) + 1;
if (i == four)
value = 4;
cell.setValue(value);
game.cells[i] = cell;
}
}
Game11.prototype.random = function(to) {
return Math.floor(Math.random() * to);
}
Game11.prototype.cellClicked = function(cell) {
if (cell.selected) {
this.collapse(cell);
} else {
this.select(cell);
}
}
Game11.prototype.collapse = function(cell) {
var newValue = cell.value + 1;
if (newValue > this.highestValue) {
this.highestValue = newValue;
}
cell.setValue(newValue);
for (var i = 24; i >= 0; i--) {
if (this.cells[i].selected) {
if (i !== cell.index) {
this.cells[i].setValue(null);
}
this.cells[i].select(false);
}
}
for (var i = 24; i >= 0; i--) {
if (this.cells[i].value == null) {
this.cells[i].collapse();
}
}
this.animate();
}
Game11.prototype.select = function(cell) {
for (var i = 0; i < 25; i++) {
this.cells[i].select(false);
}
var selectCount = 0;
var stack = [];
stack.push(cell);
while (stack.length > 0) {
var c = stack.pop();
c.select(true);
selectCount++;
var ac = this.getAdjacentCells(c);
for (var i = 0; i < ac.length; i++) {
if (ac[i].selected == false && ac[i].value == cell.value) {
stack.push(ac[i]);
}
}
}
if (selectCount == 1)
cell.select(false);
}
Game11.prototype.getAdjacentCells = function(cell) {
var result = [];
if (cell.x > 0) result.push(this.cells[cell.index - 1]);
if (cell.x < 4) result.push(this.cells[cell.index + 1]);
if (cell.y > 0) result.push(this.cells[cell.index - 5]);
if (cell.y < 4) result.push(this.cells[cell.index + 5]);
return result;
}
Game11.prototype.registerAnimation = function(animation) {
this.animations.push(animation);
}
Game11.prototype.animate = function() {
this.animating = true;
var maxTicks = 300;
var start = new Date().valueOf();
var timer = setInterval(function(){
var tick = new Date().valueOf() - start;
if (tick >= maxTicks) {
tick = maxTicks;
this.animating = false;
}
var percentage = 100 / maxTicks * tick;
for (a = 0; a < this.animations.length; a++) {
this.animations[a].step(percentage);
}
if (this.animating === false) {
clearInterval(timer);
this.animations.length = 0;
console.log('x');
}
}.bind(this), 1);
}
/**
* A single cell
*/
function Cell(game, index) {
var cell = this;
cell.game = game;
cell.index = index;
cell.selected = false;
cell.element = document.createElement('div');
cell.element.className = 'cell';
cell.digit = document.createElement('div');
cell.digit.className = 'digit';
cell.element.appendChild(cell.digit);
cell.element.addEventListener('click', cell.clicked.bind(cell));
game.element.appendChild(cell.element);
cell.x = index % 5;
cell.y = Math.floor((index - cell.x) / 5);
cell.element.style.left = (cell.x * 20) + '%';
cell.element.style.top = (cell.y * 20) + '%';
}
Cell.prototype.clicked = function() {
this.game.cellClicked(this);
}
Cell.prototype.setValue = function(value) {
this.digit.classList.remove('digit' + this.value);
this.value = value;
if (value === null) {
this.digit.innerText = '';
} else {
this.digit.classList.add('digit' + value);
this.digit.innerText = value;
}
}
Cell.prototype.select = function(selected) {
this.element.classList.toggle('selected', selected);
this.selected = selected;
}
Cell.prototype.collapse = function() {
var value, y, cellHere, cellAbove;
var n = this.y;
var offset = 0;
do {
cellHere = this.game.cells[this.x + 5*n];
y = n - offset;
value = null;
do {
if (--y >= 0) {
cellAbove = this.game.cells[this.x + 5*y];
value = cellAbove.value;
cellAbove.setValue(null);
if (value !== null) {
console.log('Value ' + value + ' for cell (' + this.x+','+n+') taken from cell ' + y);
}
} else {
offset++;
value = this.game.random(Math.max(3, this.game.highestValue - 2)) + 1;
console.log('New value ' + value + ' for cell (' + this.x+','+n+')');
}
} while (value === null);
cellHere.animateDrop(value, n-y);
} while (--n >= 0)
}
Cell.prototype.animateDrop = function(value, distance) {
this.setValue(value);
new Animation(this.game, -distance, this.index, value);
}
/**
* A cell animation
*/
function Animation(game, from, to, value) {
this.toCell = game.cells[to];
var cellBounds = this.toCell.element.getBoundingClientRect();
var fromX = toX = cellBounds.left;
var fromY = toY = cellBounds.top;
if (from < 0) {
fromY += cellBounds.height * from;
} else {
// To do: Moving from one cell to another needs an extra sprite.
this.fromCell = game.cells[from];
cellBounds = this.fromCell.element.getBoundingClientRect();
var fromX = cellBounds.left;
var fromY = cellBounds.top;
}
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
this.to = to;
game.registerAnimation(this);
}
Animation.prototype.step = function(percentage) {
var distance = this.toY - this.fromY;
var step = (100-percentage) / 100;
var Y = step * distance;
this.toCell.digit.style.top = '' + (-Y) + 'px';
}
// Start the game
new Game11(document.querySelector('.game11'));
.game11,
.game11 .cell,
.game11 .cell .digit {
box-sizing: border-box;
}
.game11 {
width: 90vmin;
height: 90vmin;
box-sizing: border-box;
position: relative;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.game11 .cell {
width: 20%;
height: 20%;
border: 2px solid #ffffff;
position: absolute;
font-size: 10vmin;
}
.game11 .cell .digit {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: 3px solid #666633;
text-align: center;
padding-top: 13%;
font-family: Impact, Charcoal, sans-serif;
color: #111111;
}
.game11 .cell.selected .digit {
color: white;
}
.game11 .digit.digit1 {
background-color: #CC66FF;
}
.game11 .digit.digit2 {
background-color: #FFCC66;
}
.game11 .digit.digit3 {
background-color: #3366FF;
}
.game11 .digit.digit4 {
background-color: #99CCFF;
}
.game11 .digit.digit5 {
background-color: #19D119;
}
.game11 .digit.digit6 {
background-color: #009999;
}
.game11 .digit.digit7 {
background-color: #996600;
}
.game11 .digit.digit8 {
background-color: #009933;
}
.game11 .digit.digit9 {
background-color: #666699;
}
.game11 .digit.digit10 {
background-color: #CC66FF;
}
.game11 .digit.digit11,
.game11 .digit.digitmax {
background-color: #FF0066;
}
<div class="game11">
</div>
You could set font size to vmin value.
.game11 .cell {
font-size: 10vmin;
}
http://jsfiddle.net/a21s77c8/
If there is no CSS solution, you can do it by JavaScript. This is quite easy, since all the game's cells are squared and the same size, so you can just get the font size as a factor of the game width. Because of these boundaries, there is no need for a complex library.
All you need to do is remove the font-size from the CSS and add this piece of code to the constructor of Game11:
// Function that calculates font size based on width of the game itself.
var updateFontSize = function() {
var bounds = game.element.getBoundingClientRect(); // Game
var size = bounds.width / 5; // Cell
size *= 0.6; // Font a bit smaller
game.element.style.fontSize = size + 'px';
};
// Attach to resize event.
window.addEventListener('resize', updateFontSize);
// Initial font size calculation.
updateFontSize();
Updated game:
/**
* Game11 class
*/
function Game11(container) {
var game = this;
game.element = container;
game.cells = [];
game.highestValue = 4;
game.animations = [];
game.animating = false;
var four = this.random(25);
// Function that calculates font size based on width of the game itself.
var updateFontSize = function() {
var bounds = game.element.getBoundingClientRect(); // Game
var size = bounds.width / 5; // Cell
size *= 0.6; // Font a bit smaller
game.element.style.fontSize = size + 'px';
};
// Attach to resize event.
window.addEventListener('resize', updateFontSize);
// Initial font size calculation.
updateFontSize();
for (var i = 0; i < 25; i++) {
var cell = new Cell(game, i);
var value = this.random(3) + 1;
if (i == four)
value = 4;
cell.setValue(value);
game.cells[i] = cell;
}
}
Game11.prototype.random = function(to) {
return Math.floor(Math.random() * to);
}
Game11.prototype.cellClicked = function(cell) {
if (cell.selected) {
this.collapse(cell);
} else {
this.select(cell);
}
}
Game11.prototype.collapse = function(cell) {
var newValue = cell.value + 1;
if (newValue > this.highestValue) {
this.highestValue = newValue;
}
cell.setValue(newValue);
for (var i = 24; i >= 0; i--) {
if (this.cells[i].selected) {
if (i !== cell.index) {
this.cells[i].setValue(null);
}
this.cells[i].select(false);
}
}
for (var i = 24; i >= 0; i--) {
if (this.cells[i].value == null) {
this.cells[i].collapse();
}
}
this.animate();
}
Game11.prototype.select = function(cell) {
for (var i = 0; i < 25; i++) {
this.cells[i].select(false);
}
var selectCount = 0;
var stack = [];
stack.push(cell);
while (stack.length > 0) {
var c = stack.pop();
c.select(true);
selectCount++;
var ac = this.getAdjacentCells(c);
for (var i = 0; i < ac.length; i++) {
if (ac[i].selected == false && ac[i].value == cell.value) {
stack.push(ac[i]);
}
}
}
if (selectCount == 1)
cell.select(false);
}
Game11.prototype.getAdjacentCells = function(cell) {
var result = [];
if (cell.x > 0) result.push(this.cells[cell.index - 1]);
if (cell.x < 4) result.push(this.cells[cell.index + 1]);
if (cell.y > 0) result.push(this.cells[cell.index - 5]);
if (cell.y < 4) result.push(this.cells[cell.index + 5]);
return result;
}
Game11.prototype.registerAnimation = function(animation) {
this.animations.push(animation);
}
Game11.prototype.animate = function() {
this.animating = true;
var maxTicks = 300;
var start = new Date().valueOf();
var timer = setInterval(function(){
var tick = new Date().valueOf() - start;
if (tick >= maxTicks) {
tick = maxTicks;
this.animating = false;
}
var percentage = 100 / maxTicks * tick;
for (a = 0; a < this.animations.length; a++) {
this.animations[a].step(percentage);
}
if (this.animating === false) {
clearInterval(timer);
this.animations.length = 0;
console.log('x');
}
}.bind(this), 1);
}
/**
* A single cell
*/
function Cell(game, index) {
var cell = this;
cell.game = game;
cell.index = index;
cell.selected = false;
cell.element = document.createElement('div');
cell.element.className = 'cell';
cell.digit = document.createElement('div');
cell.digit.className = 'digit';
cell.element.appendChild(cell.digit);
cell.element.addEventListener('click', cell.clicked.bind(cell));
game.element.appendChild(cell.element);
cell.x = index % 5;
cell.y = Math.floor((index - cell.x) / 5);
cell.element.style.left = (cell.x * 20) + '%';
cell.element.style.top = (cell.y * 20) + '%';
}
Cell.prototype.clicked = function() {
this.game.cellClicked(this);
}
Cell.prototype.setValue = function(value) {
this.digit.classList.remove('digit' + this.value);
this.value = value;
if (value === null) {
this.digit.innerText = '';
} else {
this.digit.classList.add('digit' + value);
this.digit.innerText = value;
}
}
Cell.prototype.select = function(selected) {
this.element.classList.toggle('selected', selected);
this.selected = selected;
}
Cell.prototype.collapse = function() {
var value, y, cellHere, cellAbove;
var n = this.y;
var offset = 0;
do {
cellHere = this.game.cells[this.x + 5*n];
y = n - offset;
value = null;
do {
if (--y >= 0) {
cellAbove = this.game.cells[this.x + 5*y];
value = cellAbove.value;
cellAbove.setValue(null);
if (value !== null) {
console.log('Value ' + value + ' for cell (' + this.x+','+n+') taken from cell ' + y);
}
} else {
offset++;
value = this.game.random(Math.max(3, this.game.highestValue - 2)) + 1;
console.log('New value ' + value + ' for cell (' + this.x+','+n+')');
}
} while (value === null);
cellHere.animateDrop(value, n-y);
} while (--n >= 0)
}
Cell.prototype.animateDrop = function(value, distance) {
this.setValue(value);
new Animation(this.game, -distance, this.index, value);
}
/**
* A cell animation
*/
function Animation(game, from, to, value) {
this.toCell = game.cells[to];
var cellBounds = this.toCell.element.getBoundingClientRect();
var fromX = toX = cellBounds.left;
var fromY = toY = cellBounds.top;
if (from < 0) {
fromY += cellBounds.height * from;
} else {
// To do: Moving from one cell to another needs an extra sprite.
this.fromCell = game.cells[from];
cellBounds = this.fromCell.element.getBoundingClientRect();
var fromX = cellBounds.left;
var fromY = cellBounds.top;
}
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
this.to = to;
game.registerAnimation(this);
}
Animation.prototype.step = function(percentage) {
var distance = this.toY - this.fromY;
var step = (100-percentage) / 100;
var Y = step * distance;
this.toCell.digit.style.top = '' + (-Y) + 'px';
}
// Start the game
new Game11(document.querySelector('.game11'));
.game11,
.game11 .cell,
.game11 .cell .digit {
box-sizing: border-box;
}
.game11 {
width: 90vw;
height: 90vw;
max-width: 90vh;
max-height: 90vh;
box-sizing: border-box;
position: relative;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.game11 .cell {
width: 20%;
height: 20%;
border: 2px solid #ffffff;
position: absolute;
}
.game11 .cell .digit {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: 3px solid #666633;
text-align: center;
padding-top: 13%;
font-family: Impact, Charcoal, sans-serif;
color: #111111;
}
.game11 .cell.selected .digit {
color: white;
}
.game11 .digit.digit1 {
background-color: #CC66FF;
}
.game11 .digit.digit2 {
background-color: #FFCC66;
}
.game11 .digit.digit3 {
background-color: #3366FF;
}
.game11 .digit.digit4 {
background-color: #99CCFF;
}
.game11 .digit.digit5 {
background-color: #19D119;
}
.game11 .digit.digit6 {
background-color: #009999;
}
.game11 .digit.digit7 {
background-color: #996600;
}
.game11 .digit.digit8 {
background-color: #009933;
}
.game11 .digit.digit9 {
background-color: #666699;
}
.game11 .digit.digit10 {
background-color: #CC66FF;
}
.game11 .digit.digit11,
.game11 .digit.digitmax {
background-color: #FF0066;
}
<div class="game11">
</div>
Still, if it could be done through CSS, that'd be great.