Add CSS style to a specific class via javascript - javascript

I got these lines of code and want to add them only to a specific div class. So if the background is dark it should be filter: invert(1); and when the background is brighter filter: invert(0);. How can I do that? Hope you can help me...I'm still at the very beginning of understanding js.
function isDark( color ) {
var match = /rgb\((\d+).*?(\d+).*?(\d+)\)/.exec(color);
return ( match[1] & 255 )
+ ( match[2] & 255 )
+ ( match[3] & 255 )
< 3 * 256 / 2;
}
$('div').each(function() {console.log($(this).css("background-color"))
$(this).css("filter", isDark($(this).css("background-color")) ? 'invert(1)' : 'invert(0)');
});

You can check the condition separately in an if else statement as below.
$('div').each(function() {
var color = $(this).css("background-color");
if (isDark(color)) {
$(this).css("filter", 'invert(1)');
} else {
$(this).css("filter", 'invert(0)');
}
});
OR
if you want a css class to be added, you can do it as below
$('div').each(function() {
var color = $(this).css("background-color");
if (isDark(color)) {
$(this).addClass("inverted");
} else {
$(this).addClass("not-inverted");
}
});
in which the styles can be added for the new class in your CSS as below.
.inverted {
filter: invert(1);
}
.not-inverted {
filter: invert(0);
}
See the snippet below.
function isDark(color) {
var match = /rgb\((\d+).*?(\d+).*?(\d+)\)/.exec(color);
return (match[1] & 255) +
(match[2] & 255) +
(match[3] & 255) <
3 * 256 / 2;
}
/*
$('div').each(function() {console.log($(this).css("background-color"))
$(this).css("filter", isDark($(this).css("background-color")) ? 'invert(1)' : 'invert(0)');
}); */
$('div').each(function() {
var color = $(this).css("background-color");
if (isDark(color)) {
$(".logo").css("filter", 'invert(1)');
} else {
$(".logo").css("filter", 'invert(0)');
}
});
body {
height: 400vh;
}
.logo {
position: fixed;
top: 40px;
right: 20px;
background-image: url(https://image.freepik.com/free-vector/bicycle-shop-logo-design-vector_53876-40626.jpg);
background-repeat: no-repeat;
background-size: 100px;
height: 100px;
width: 100px;
background-color: black;
}
.blabla {
background-color: black;
height: 50vh;
}
.blablabla {
background-color: grey;
height: 50vh;
}
.blablablabla {
background-color: red;
height: 50vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="logo"></div>
<div class="blabla">test</div>
<div class="blablabla">test</div>
<div class="blablablabla">test</div>

Related

elem.getBoundingClientRect() broken on mobile screen

I'm developing a website using gatsby.js and it involves a slide-in animation as you scroll down. I wrote code that worked perfectly until I opened dev tools and tried it using the device toolbar.
here's a reproduction demo as well as a webpage to make it easier
https://getboundingclientrect-is-broken.netlify.app
<div class="0 space"></div>
<p class="1 slideFR"></p>
<div id="boy" class="2 slideFL"></div>
<p class="3 slideFR"></p>
<div class="4 slideFL"></div>
<div class="flx space"></div>
.slideFR {
width: 100px;
height: 100px;
background-color: #957b26;
position: relative;
left: 450px;
transform: translateX(1000px);
}
.slideFL {
width: 100px;
height: 100px;
background-color: #26958f;
position: relative;
left: 300px;
transform: translateX(-1000px);
}
.inSight {
transition: all 0.5s;
transform: translateX(0);
}
.space {
width: 100px;
height: 1500px;
background-color: aquamarine;
}
let elemsFL = document.getElementsByClassName("slideFL");
var leftiesLoaded = Array.from( { length: elemsFL.length }, (_, i) => false ); // creates array length of elemsFL full of <false>
let elemsFR = document.getElementsByClassName("slideFR");
var rightersLoaded = Array.from( { length: elemsFR.length }, (_, i) => false ); // creates array length of elemsFR full of <false>
document.addEventListener("scroll", function (event) {
let windowHeight = window.outerHeight;
console.log( "%c/* ----------- scroll ---------- */", "color: purple; font-weight: bold" );
checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
checkIfInSight(elemsFR, rightersLoaded, windowHeight);
});
/* -------------------------------- touchmove ------------------------------- */
document.addEventListener("touchmove", function (event) {
let windowHeight = window.outerHeight;
console.log( "%c/* ---------- touchmove --------- */", "color: red; font-weight: bold" );
checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
checkIfInSight(elemsFR, rightersLoaded, windowHeight);
});
function checkIfInSight(elemArray, boolArray, windowHeight) {
for (let counter = 0; counter < elemArray.length; counter++) {
const elem = elemArray[counter];
let elemRect = elem.getBoundingClientRect();
let elemPosTop = elemRect.top;
let elemPosBottom = elemPosTop + elem.scrollHeight;
if (elemPosTop <= windowHeight && elemPosBottom >= 0) {
if (!boolArray[counter]) {
console.log( "%c In Sight", "color: green", elem.classList[0] );
boolArray[counter] = true;
elem.classList.add("inSight");
} else {
console.log( "%c In Sight And Loaded", "color: yellow", elem.classList[0] );
}
} else {
console.log( elem.classList[0], "\tOut Of Sight", elemPosTop, "<=", windowHeight, "&&", elemPosBottom, ">=0\t\t\t", elem.offsetTop );
boolArray[counter] = false;
elem.classList.remove("inSight");
}
}
}
Edit:
As I'm troubleshooting this I replaced elem.offsetTop with window.scrollY which indeed made me realize that for some reason the it is not interpreting the scroll action as actually scrolling for quite a while. I still don't know what I'm doing wrong or what the issue is
thanks to EmielZuurbier's comment I found the solution IntersectionObserver API was the way to go. I even produced cleaner more optimized code.
HTML
<div class="0 space"></div>
<p class="1 slideFR toSlide"></p>
<div id="boy" class="2 slideFL toSlide"></div>
<p class="3 slideFR toSlide"></p>
<div class="4 slideFL toSlide"></div>
<div class=" space"></div>
JS
const slideDivs = document.querySelectorAll(".toSlide");
const options={
root: null,
rootMargin: "0px 2000px",
};
const observer= new IntersectionObserver(function(entries, observer){
entries.forEach(entry =>{
console.log(entry.target.classList[0],entry.isIntersecting, entry.intersectionRect);
if (entry.isIntersecting ){
entry.target.classList.add("inSight");
}else {
entry.target.classList.remove("inSight");
}
});
},options);
slideDivs.forEach(slideDiv => {
observer.observe(slideDiv);
});
CSS
.slideFR {
width: 100px;
height: 100px;
background-color: #957b26;
position: relative;
left: 200px;
transform: translateX(1000px);
}
.slideFL {
width: 100px;
height: 100px;
background-color: #26958f;
position: relative;
left: 150px;
/* visibility: hidden; */
transform: translateX(-1000px);
}
.inSight {
transition: all 0.5s;
transform: translateX(0);
}
.space {
width: 100px;
height: 1500px;
background-color: aquamarine;
}

How can I get jQuery code working on touch screen device

I use some jQuery code for my range slider, it works well on PC but not working on a touch device and I don't know how to fix it.
I have tried different ways such as:
_ Use on('touchmove', rangeInputChangeEventHandler); instead of on('input', rangeInputChangeEventHandler); but it still not working
_ Add $('input[type="range"]').on( 'touchstart', rangeInputChangeEventHandler); below $('input[type="range"]').on( 'input', rangeInputChangeEventHandler); line and it's still not working.
Here is my code, it includes Javascript, jQuery, HTML, and CSS to run code:
Javascript
//main function change number of slider when dragging
(function() {
// function to round up number
function lamtronTien(number) {
// set rounded, currentForamt, tienFormat as varibale
var rounded = Math.round(number / 1000000) * 1000000;
var currentFormat = new Intl.NumberFormat("vn-VN");
var tienFormat = currentFormat.format(rounded);
//
return tienFormat;
}
//function to turn number into word
function docSoTien(number) {
// replace "," with ""
number = number.replaceAll(",", "");
// set variable ty and trieu as below
var ty = Math.pow(10, 9);
var trieu = Math.pow(10, 6);
// when number = 0 return 0
if (number === "0") {
return 0;
// else number is larger than ty
} else {
if (number >= ty) {
// set variable as floor of "number" devided by "ty"
var hangTy = Math.floor(number / ty);
// if hangTy is greater than 1 then result
if (hangTy >= 1) {
// set varibales
var hangTrieu = number % ty;
var hangTrieu = Math.floor(hangTrieu / trieu);
if (hangTrieu > 0) {
return (hangTy + " Tỷ " + hangTrieu + " Triệu");
// else
} else {
return (hangTy + " Tỷ");
}
}
} else {
// var hangTrieu = number % ty;
var hangTrieu = Math.floor(number / trieu);
if (hangTrieu > 0) {
return (hangTrieu + " Triệu");
}
}
}
}
// function to apply change when there is a change in number input
function rangeInputChangeEventHandler(e) {
// set attribute name as rangeGroup
var rangeGroup = $(this).attr('name'),
minBtn = $(this).parent().children('.min'),
maxBtn = $(this).parent().children('.max'),
// get value of range_min, range_max
range_min = $(this).parent().children('.range_min'),
range_max = $(this).parent().children('.range_max'),
minVal = parseInt($(minBtn).val()),
maxVal = parseInt($(maxBtn).val()),
origin = e.originalEvent.target.className;
// if origin
if (origin === 'min' && minVal > maxVal - 5) {
$(minBtn).val(maxVal - 5);
}
var minVal = parseInt($(minBtn).val());
$(range_min).html(docSoTien(lamtronTien(minVal)));
// if origin is max and maxVal minus 5 is smaller than minVal
if (origin === 'max' && maxVal - 5 < minVal) {
$(maxBtn).val(5 + minVal);
}
var maxVal = parseInt($(maxBtn).val());
if (docSoTien(lamtronTien(maxVal)) === '3 Tỷ') {
$(range_max).html('> 3 Tỷ');
} else {
// apply to range_max
$(range_max).html(docSoTien(lamtronTien(maxVal)));
};
}
// apply change to input type range
$('input[type="range"]').on('input', rangeInputChangeEventHandler);
})();
/* adjust */
input[type='range'] {
width: 210px;
height: 30px;
padding: 20px;
overflow: hidden;
cursor: pointer;
outline: none;
}
/* adjust */
input[type='range'],
input[type='range']::-webkit-slider-runnable-track,
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
background: none;
}
/* adjust */
input[type='range']::-webkit-slider-runnable-track {
width: 300px;
height: 1px;
background: #003D7C;
}
/* adjust */
input[type='range']:nth-child(2)::-webkit-slider-runnable-track {
background: none;
}
/* adjust */
input[type='range']::-webkit-slider-thumb {
position: relative;
height: 15px;
width: 15px;
margin-top: -7px;
background: #fff;
border: 1px solid #003D7C;
border-radius: 25px;
z-index: 1;
}
/* adjust */
input[type='range']:nth-child(1)::-webkit-slider-thumb {
z-index: 2;
}
.rangeslider {
height: 60px;
width: 210px;
display: inline-block;
margin-top: -5px;
margin-left: 20px;
}
/* adjust rangeslider input */
.rangeslider input {
position: absolute;
}
/* adjust rangeslider */
.rangeslider {
position: absolute;
}
/* adjust rangeslider span*/
.rangeslider span {
margin-top: 30px;
left: 0;
}
/* adjust rangeslider right thumb */
.rangeslider .right {
position: relative;
float: right;
margin-right: -70px;
margin-top: -3px;
}
<!-- slider -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- slider -->
<div class="slider-group">
<div class="rangeslider">
<input class="min" name="min" type="range" min="0" max="3000000000" value="0" />
<input class="max" name="max" type="range" min="0" max="3000000000" value="3000000000" />
<span class="range_min light left">0</span>
<span class="range_max light right"> > 3 Tỷ</span>
</div>
</div>
and my site: link
Can anyone tell me what is the problem and how to fix it? Thank you!

Check if DOM elements are present inside a DIV then run functions assigned to those elements in order

i'm trying to develop a game using html, css and js. At the moment I'm focusing on manipulating DOM elements without using the canvas tag. My idea is to create a pseudo graphical programming language, similar to the Blockly environment. So far I have inserted 3 clickable elements inside #toolbox that create their copies in #workspace.
Now, I am trying to assign functions to the elements present in #workspace, which once pressed the Run button are executed in order of appearance, so as to create a queue of commands that is able to move the pink square inside #output_section.
Therefore I cannot understand how to write the function that is able to verify the presence of the elements and then be able to perform the different functions assigned to these elements.
Any ideas? :D
I'm using Jquery 3.3.1
function addRed() {
var redWorkspace = document.createElement("DIV");
redWorkspace.className = "remove-block block red";
document.getElementById("workspace").appendChild(redWorkspace);
};
function addBlue() {
var blueWorkspace = document.createElement("DIV");
blueWorkspace.className = "remove-block block blue";
document.getElementById("workspace").appendChild(blueWorkspace);
};
function addGreen() {
var greenWorkspace = document.createElement("DIV");
greenWorkspace.className = "remove-block block green";
document.getElementById("workspace").appendChild(greenWorkspace);
};
$("#clear_workspace").click(function () {
$("#workspace").empty();
});
$(document).on("click", ".remove-block", function () {
$(this).closest("div").remove();
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
bottom: 0;
right: 0;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div onclick="addRed()" class="block red">Left</div>
<div onclick="addBlue()" class="block blue">Up</div>
<div onclick="addGreen()" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>
</body>
</html>
Completely untested but run button does something along the lines of:
$("#run_workspace").click(function() {
$("#workspace .block").each(function(elem) {
if (elem.hasClass("red")) {
moveObjectLeft();
} else if (elem.hasClass("green")) {
moveObjectRight();
} else if (elem.hasClass("blue")) {
moveObjectUp();
}
});
});
Commonly, it's a good idea to store all required information in arrays and objects, and use HTML only to display your data.
Also, if you are already using jQuery - use it for all 100%)
Made some improvements:
let mobs = {
pinky: {
node: $('#moving_square'),
coors: { top: 400, left: 400 },
step: 30,
moveQueue: [],
// moveTimeout ???
},
}; // storing here all created objects, that must move.
/* Each [moveQueue] array will store the chain of moves, like ["up", "up", "left"]
You can take each "key-word" of move, and get required function buy that key,
from the 'move' object */
let move = { // Think about how to simlify this object and functions. It's possible!)
left: function (obj) {
let left = obj.coors.left = (obj.coors.left - obj.step);
obj.node.css('left', left + 'px');
},
up: function (obj) {
let top = obj.coors.top = (obj.coors.top - obj.step);
obj.node.css('top', top + 'px');
},
right: function (obj) {
let left = obj.coors.left = (obj.coors.left + obj.step);
obj.node.css('left', left + 'px');
}
};
let stepTimeout = 1000;
let running = false;
let timeouts = {}; // store all running timeouts here,
// and clear everything with for( key in obj ) loop, if required
$('#toolbox .block').on('click', function () {
let color = $(this).attr('data-color');
let workBlock = '<div class="remove-block block ' + color + '"></div>';
$('#workspace').append(workBlock);
mobs.pinky.moveQueue.push( $(this).text().toLowerCase() ); // .attr('data-direction');
// instead of pinky - any other currently selected object
// $(this).text().toLowerCase() — must be "left", "up", "right"
});
$('#run_workspace').on('click', function () {
running = true;
runCode();
function runCode() {
for (let obj in mobs) { // mobile objects may be multiple
// Inside the loop, obj == mobs each key name. Here it's == "pinky"
let i = 0;
let pinky = mobs[obj];
localRun();
function localRun() {
let direction = pinky.moveQueue[i]; // getting direction key by array index.
move[direction](pinky); // calling the required function from storage.
if (pinky.moveQueue[++i] && running ) {
// self-calling again, if moveQueue has next element.
// At the same time increasing i by +1 ( ++i )
timeouts[obj] = setTimeout(localRun, stepTimeout);
}
}
}
}
});
$("#clear_workspace").click(function () {
$("#workspace").empty();
});
$('#workspace').on("click", ".remove-block", function () {
$(this).closest("div").remove();
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
top: 400px;
left: 400px;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div data-color="red" class="block red">Left</div>
<div data-color="blue" class="block blue">Up</div>
<div data-color="green" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>
But... jQuery was used only for clicks... Translation to JS:
let mobs = {
pinky: {
node: document.getElementById('moving_square'),
coors: { top: 400, left: 400 },
step: 30,
moveQueue: [],
},
};
let move = {
left: function (obj) {
let left = obj.coors.left = (obj.coors.left - obj.step);
obj.node.style.left = left + 'px';
},
up: function (obj) {
let top = obj.coors.top = (obj.coors.top - obj.step);
obj.node.style.top = top + 'px';
},
right: function (obj) {
let left = obj.coors.left = (obj.coors.left + obj.step);
obj.node.style.left = left + 'px';
}
};
let stepTimeout = 1000;
let running = false;
let timeouts = {};
let blocks = document.querySelectorAll('#toolbox .block');
let workSpace = document.getElementById('workspace');
blocks.forEach(function(block){
block.addEventListener('click', function(){
let color = this.dataset.color;
let workBlock = '<div class="remove-block block ' + color + '"></div>';
workSpace.insertAdjacentHTML('beforeend', workBlock);
mobs.pinky.moveQueue.push( this.textContent.toLowerCase() );
});
});
document.getElementById('run_workspace').addEventListener('click', function () {
running = true;
runCode();
function runCode() {
for (let obj in mobs) { // mobile objects may be multiple
// Inside the loop, obj == mobs each key name. Here it's == "pinky"
let i = 0;
let pinky = mobs[obj];
localRun();
function localRun() {
let direction = pinky.moveQueue[i]; // getting direction key by array index.
move[direction](pinky); // calling the required function from storage.
if (pinky.moveQueue[++i] && running ) {
// self-calling again, if moveQueue has next element.
// At the same time increasing i by +1 ( ++i )
timeouts[obj] = setTimeout(localRun, stepTimeout);
}
}
}
}
});
document.getElementById("clear_workspace").addEventListener('click', function () {
workSpace.textContent = "";
});
workSpace.addEventListener('click', function (e) {
if( e.target.classList.contains('remove-block') ){
e.target.remove();
}
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
top: 400px;
left: 400px;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div data-color="red" class="block red">Left</div>
<div data-color="blue" class="block blue">Up</div>
<div data-color="green" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>

DIVs split into half and added as a child in a loop

I was asked this question during an interview recently.
Problem statement - Given a div of a certain width and height, keep appending divs to it, but size it down to half before the append. Do it until height/width is less than 10. Attached is my solution
let toggle = true;
let border = 0;
container = $('.mainBox');
while (container.height() > 10 || container.width() > 10) {
if(toggle) {
container = splitVertically(container);
toggle = false;
} else {
container = splitHorizontally(container);
toggle = true;
}
}
function splitVertically(container) {
let $newElem = $('<div>')
.width(container.width()/2)
.height(container.height())
.css('border-right', 'solid 1px');
container.append($newElem);
return $newElem;
}
function splitHorizontally(container) {
let $newElem = $('<div>')
.height(container.height()/2)
.width(container.width())
.css('border-bottom', 'solid 1px');
container.append($newElem);
return $newElem;
}
.mainBox {
width: 500px;
height: 200px;
/* background: blue; */
border: 1px solid;
}
.addBorder {
border: 1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="mainBox"></div>
If I wanted to add the divs to right of parent how would I do that?
Thanks!
You can try by putting border-left and float:right
let toggle = true;
let border = 0;
container = $('.mainBox');
while (container.height() > 10 || container.width() > 10) {
if (toggle) {
container = splitVertically(container);
toggle = false;
} else {
container = splitHorizontally(container);
toggle = true;
}
}
function splitVertically(container) {
let $newElem = $('<div class="pull-right">')
.width(container.width() / 2)
.height(container.height())
.css('border-left', 'solid 1px');
container.append($newElem);
return $newElem;
}
function splitHorizontally(container) {
let $newElem = $('<div class="">')
.height(container.height() / 2)
.width(container.width())
.css('border-bottom', 'solid 1px');
container.append($newElem);
return $newElem;
}
.mainBox {
width: 500px;
height: 200px;
/* background: blue; */
border: 1px solid;
}
.addBorder {
border: 1px solid;
}
.pull-right {
float: right;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="mainBox"></div>
Use after in place of append and also add a class name to the div you are adding and then in its style make display: inline-block; Check the below snippet in full page mode to see it in work.
let toggle = true;
let border = 0;
container = $('.mainBox');
while (container.height() > 10 || container.width() > 10) {
if(toggle) {
container = splitVertically(container);
toggle = false;
} else {
container = splitHorizontally(container);
toggle = true;
}
}
function splitVertically(container) {
let $newElem = $('<div class="mainBox">')
.width(container.width()/2)
.height(container.height())
.css('border-right', 'solid 1px');
container.after($newElem);
return $newElem;
}
function splitHorizontally(container) {
let $newElem = $('<div class="mainBox">')
.height(container.height()/2)
.width(container.width())
.css('border-bottom', 'solid 1px');
container.after($newElem);
return $newElem;
}
.mainBox {
width: 500px;
height: 200px;
/* background: blue; */
border: 1px solid;
display: inline-block;
}
.addBorder {
border: 1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="mainBox"></div>
Hope this helps
Adding float: left to the CSS for your child divs will allow that. This version also shows an example that doesn't require jQuery
let box = document.querySelector('.mainBox'), w = box.clientWidth, h = box.clientHeight
function px(val){ return [val, 'px'].join('') }
while(Math.max(w, h) > 10){
let child = document.createElement('div')
if(w > h) w /= 2
else h /= 2
child.style.width = px(w)
child.style.height = px(h)
box.appendChild(child)
}
div {
border: 1px solid black;
vertical-align: top;
box-sizing: border-box;
}
.mainBox {
width: 500px;
height: 200px;
/* background: blue; */
}
.mainBox > div {
float: left;
}
<div class="mainBox"></div>

Semi-fixed text in a scrolling container

I've got a bunch of horizontal boxes containing text. The boxes are all in a horizontally scrolling container:
// generate some random data
var model = {
leftEdge: ko.observable(0)
};
model.rows = populateArray(10 + randInt(20), randRow);
ko.applyBindings(model);
$(function() {
$('.slide').on('scroll', function() {
model.leftEdge(this.scrollLeft);
})
})
function randRow() {
var events = populateArray(50 + randInt(100), randEvent);
var left = randInt(1000);
events.forEach(function(event) {
event.left = left;
left += 10 + event.width + randInt(1000);
});
return {
events: events
}
}
function randEvent() {
var word = randWord()
var width = 50 + Math.max(8 * word.length, randInt(200));
var event = {
left: 0,
width: width,
label: word
};
event.offset = ko.computed(function() {
// reposition the text to stay
// * within its container
// * fully on-screen (if possible)
var leftEdge = model.leftEdge();
return Math.max(0, Math.min(
leftEdge - event.left,
event.width - 8 * event.label.length
));
});
return event;
}
function randWord() {
var n = 2 + randInt(5);
var ret = "";
while (n-- > 0) {
ret += randElt("rmhntsk");
ret += randElt("aeiou");
}
return ret;
}
function randElt(arr) {
return arr[randInt(arr.length)];
}
function populateArray(n, populate) {
var arr = new Array(n);
for (var i = 0; i < n; i++) {
arr[i] = populate();
}
return arr;
}
function randInt(n) {
return Math.floor(Math.random() * n);
}
.slide {
max-width: 100%;
overflow: auto;
border: 5px solid black;
}
.row {
position: relative;
height: 25px;
}
.event {
position: absolute;
top: 2.5px;
border: 1px solid black;
padding: 2px;
background: #cdffff;
font-size: 14px;
font-family: monospace;
}
.event > span {
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="slide" data-bind="foreach: rows">
<div class="row" data-bind="foreach: events">
<div class="event" data-bind="style: { left: left+'px', width: width+'px' }"><span data-bind="text:label, style: { left: offset() + 'px' }"></div>
</div>
</div>
What I'd like to do is as the user scrolls from left-to-right, reposition the text within each box that partially overlaps the left border of the visible window to keep the text as visible as possible.
Currently I'm doing this by manually repositioning each item of text.
Is there a cleaner way to do this using CSS?
A friend helped me come up with this solution.
In English, the idea is to add an overlay to each row that is positioned relatively to the frame of the scrolling box, rather than the contents.
Then we can place a label for any box that overlaps the left edge in this overlay and it will appear to smoothly move as the box underneath it scrolls.
// generate some random data
var model = {
leftEdge: ko.observable(0),
};
model.rows = populateArray(10 + randInt(20), randRow);
model.width = Math.max.apply(Math, $.map(model.rows, function(row) {
return row.width
}));
ko.applyBindings(model);
$(function() {
$('.slide').on('scroll', function() {
model.leftEdge(this.scrollLeft);
})
})
function randRow() {
var events = populateArray(50 + randInt(100), randEvent);
var left = randInt(1000);
events.forEach(function(event) {
event.left = left;
left += 10 + event.width + randInt(1000);
});
return {
events: events,
width: left
}
}
function randEvent() {
var word = randWord()
var width = 50 + Math.max(8 * word.length, randInt(200));
var event = {
width: width,
label: word,
};
event.tense = ko.computed(function() {
// reposition the text to stay#
// * within its container
// * fully on-screen (if possible)
var leftEdge = model.leftEdge();
return ['future', 'present', 'past'][
(leftEdge >= event.left) +
(leftEdge > event.left + event.width - 8 * event.label.length)
];
});
return event;
}
function randWord() {
var n = 2 + randInt(5);
var ret = "";
while (n-- > 0) {
ret += randElt("rmhntsk");
ret += randElt("aeiou");
}
return ret;
}
function randElt(arr) {
return arr[randInt(arr.length)];
}
function populateArray(n, populate) {
var arr = new Array(n);
for (var i = 0; i < n; i++) {
arr[i] = populate();
}
return arr;
}
function randInt(n) {
return Math.floor(Math.random() * n);
}
.wrapper {
position: relative;
border: 5px solid black;
font-size: 14px;
font-family: monospace;
}
.slide {
max-width: 100%;
overflow: auto;
}
.slide > * {
height: 25px;
}
.overlay {
position: absolute;
width: 100%;
left: 0;
}
.overlay .past {
display: none
}
.overlay .present {
position: absolute;
z-index: 1;
top: 5.5px;
left 0;
}
.overlay .future {
display: none
}
.row {
position: relative;
}
.event {
position: absolute;
top: 2.5px;
border: 1px solid black;
padding: 2px;
background: #cdffff;
height: 14px;
}
.event .past {
float: right;
}
.event .present {
display: none;
}
.event .future {
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="wrapper">
<div class="slide" data-bind="foreach: rows, style: { width: width + 'px' }">
<div class="overlay" data-bind="foreach: events">
<span data-bind="text:label, css: tense"></span>
</div>
<div class="row" data-bind="foreach: events">
<div class="event" data-bind="style: { left: left+'px', width: width+'px' }"><span data-bind="text:label, css: tense"></div>
</div>
</div></div>
This doesn't result in less javascript, but it does result in more efficient javascript, as class changes happen much less often than offset changes, so fewer updates to DOM elements are required.
You can avoid processing every "event" (in the above example) by doing some pre-partitioning of the horizontal space, and only updating events in the relevant partition.

Categories

Resources