January 5, 2023: Update and recommended solution at the bottom.
Using plain Javascript or Typescript and no other dependencies, how would the following be implemented?
Two or more DIVs with different content lengths
Scroll at the same rate
Mouse pointer can be over any of the DIVs
When the shorter content DIV (SCD) runs out of content, it stops scrolling but the other DIVs continue to scroll. However, if scrolling reverses direction, the SCD starts reversing its direction until it runs out of content while the other DIVs continue to scroll.
The dozens of implementations of simultaneous multiple DIVs scrolls aren't able to do the above because:
Some assume the content to be the same length.
Only works if the mouse is scrolling over the DIV with the longer content. When the mouse is scrolling over the DIV with shorter content, scrolling stops for both after the SCD runs out of content.
Have DIVs move at different speeds to match the top and bottom of the content which is close to the requirements.
One previous discussion on a similar topic had some solutions that came close, especially a comment by 'Gregor y'. Unfortunately, that solution had both DIVs move at different speeds. The simplified Fiddle for their implementation is available.
function syncOnScroll(selector,SyncFn) {
if(!SyncFn)return;
let active = null;
document.querySelectorAll(selector).forEach((div) => {
div.addEventListener("mouseenter", (e) => {active = e.target});
div.addEventListener("scroll", (e) => {
//ignore inactive-scroll events
if (e.target !== active) return;
//push the active-scroll event to synced elements
document.querySelectorAll(selector).forEach((target) => {
if (active === target) return;
SyncFn(target,active);
});
});
});
}
function scrollSync(selector,scrollType) {
let type = scrollType || 'both';
function calcPosFn(position,fromRange,fromWidth,toRange,toWidth){
if(fromRange == fromWidth) return 0;
return Math.floor(
position * ((toRange-toWidth)/
(fromRange-fromWidth))
);
}
switch(type.toLowerCase()){
case 'vertical':
syncOnScroll(selector,function(t,a){
t.scrollTop=calcPosFn(
a.scrollTop,
a.scrollHeight,
a.clientHeight,
t.scrollHeight,
t.clientHeight)});break;
case 'horizontal':
syncOnScroll(selector,function(t,a){
t.scrollLeft=calcPosFn(
a.scrollLeft,
a.scrollWidth,
a.clientWidth,
t.scrollWidth,
t.clientWidth)});break;
case 'both':default:
syncOnScroll(selector,function(t,a){
t.scrollTop = calcPosFn(
a.scrollTop,
a.scrollHeight,
a.clientHeight,
t.scrollHeight,
t.clientHeight);
t.scrollLeft = calcPosFn(
a.scrollLeft,
a.scrollWidth,
a.clientWidth,
t.scrollWidth,
t.clientWidth);
});break;
}
}
//RWM: Call the function on the elements you need synced.
scrollSync(".scrollSyncV",'vertical');
/* use whatever method you want to put the tables side-by-side */
:root {
padding: 1rem;
}
.scrollSyncV {
float: left;
width: 50%;
height: 10rem;
overflow: auto;
border: 1px solid black;
box-sizing: border-box;
}
table {
width: 100%;
}
tr:nth-child(even) td {
background-color: #eef;
}
<div class="scrollSyncV" id="1">
<table>
<th>Longer</th>
<tr><td>Left Line 1</td></tr>
<tr><td>Left Line 2</td></tr>
<tr><td>Left Line 3</td></tr>
<tr><td>Left Line 4</td></tr>
<tr><td>Left Line 5</td></tr>
<tr><td>Left Line 6</td></tr>
<tr><td>Left Line 7</td></tr>
<tr><td>Left Line 8</td></tr>
<tr><td>Left Line 9</td></tr>
<tr><td>Left Line 10</td></tr>
<tr><td>Left Line 11</td></tr>
<tr><td>Left Line 12</td></tr>
<tr><td>Left Line 13</td></tr>
<tr><td>Left Line 14</td></tr>
<tr><td>Left Line 15</td></tr>
<tr><td>Left Line 16</td></tr>
<tr><td>Left Line 17</td></tr>
<tr><td>Left Line 18</td></tr>
<tr><td>Left Line 19</td></tr>
<tr><td>Left Line 20</td></tr>
<tr><td>Left Line 21</td></tr>
<tr><td>Left Line 22</td></tr>
<tr><td>Left Line 23</td></tr>
<tr><td>Left Line 24</td></tr>
<tr><td>Left Line 25</td></tr>
</table>
</div>
<div class="scrollSyncV" id="2">
<table>
<th>Shorter</th>
<tr><td>Right Line 1</td></tr>
<tr><td>Right Line 2</td></tr>
<tr><td>Right Line 3</td></tr>
<tr><td>Right Line 4</td></tr>
<tr><td>Right Line 5</td></tr>
<tr><td>Right Line 6</td></tr>
<tr><td>Right Line 7</td></tr>
<tr><td>Right Line 8</td></tr>
<tr><td>Right Line 9</td></tr>
<tr><td>Right Line 10</td></tr>
</table>
</div>
<div id="fiddleBottomPadding" style="height:100px"></div>
Update:
#Roko's fiddle pretty much answered my question as was stated. He did make a note about fast scrolling. Thanks, Roko!
Based on further discussion with #Roko about Twitter's scroll implementation and his mention of using CSS sticky, a solution by #rootShiv that uses CSS sticky was found that met the concept I was after.
Edit: The closest I got to a solution is this jsFiddle example.
To give you a starting point (no manual scrollbars scroll implemented yet)
by using the "wheel" event, preventing the default scroll behavior by using Event.preventDefault() and by applying a delta and by using Element.scrollBy()
const elsDIV = document.querySelectorAll(".scrollSyncV");
elsDIV.forEach(el => {
const elOther = [...elsDIV].filter(e => e !== el)[0];
el.addEventListener('wheel', evt => {
evt.preventDefault();
const delta = Math.sign(evt.deltaY);
elsDIV.forEach(el => el.scrollBy({top: 90.909 * delta}))
});
});
body {
display: flex;
}
.scrollSyncV {
width: 50%;
height: 10rem;
overflow: auto;
border: 1px solid black;
box-sizing: border-box;
font-size: 2.5rem;
}
.scrollSyncV div {
border: 10px dashed #aaa;
}
<div class="scrollSyncV">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Consequuntur rerum rem quae iste earum aspernatur.
Laudantium eum, animi maiores unde itaque,
repellat sed magni dicta earum alias, ipsa ad labore!
Deserunt recusandae modi. Earum autem provident eum in officia.
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Iure possimus doloribus veritatis dolores voluptate quae
fuga eaque mollitia magni nam exercitationem ratione itaque
est debitis dolor, nemo repudiandae voluptas sequi!
<br>
End.
</div>
<div class="scrollSyncV">
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Consequuntur rerum rem quae iste earum aspernatur.
Laudantium eum, animi maiores unde itaque,
repellat sed magni dicta earum alias, ipsa ad labore!
<br>End.
</div>
Related
As an exercise I am trying to create a small quiz app and a part of it are the question cards. On these cards I have a question and then a button to show the answer. When the button is clicked, then the answer (which doesn't exist in the HTML DOM yet, therefore not visible) will show up and with the next click, the answer should be hidden again. Basically it will look something like this:
Before Show Answer is clicked
After Show Answer is clicked
Here is the HTML code:
<section class="question-card">
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam vitae
labore repudiandae tenetur. Qui maiores animi quibusdam voluptatum
nobis. Nam aperiam voluptatum dolorem quia minima assumenda velit libero
saepe repellat. Tempore delectus deleniti libero aliquid rem velit illum
expedita nostrum quam optio maiores officiis consequatur ea, sint enim
cum repudiandae inventore ab nemo?
</p>
<div class="bookmark">
<i class="fa-regular fa-bookmark fa-lg"></i>
</div>
<button class="answer-button" data-js="answer-button">Show Answer</button>
<ul class="answer-container" data-js="answer-container">
</ul>
<div class="container-categories">
<button class="category-button category-html">#html</button>
<button class="category-button category-flexbox">#flexbox</button>
<button class="category-button category-css">#css</button>
<button class="category-button category-js">#js</button>
</div>
</section>
I have added an EventListener for the Show Answer button that adds a list item in the already existing ul when it is clicked. I have done this with innerHTML:
const answerButton = document.querySelector(".answer-button");
const answerContainer = document.querySelector(".answer-container");
const answer1 = "Lorem ipsum dolor sit amet consectetur adipisicing elit.";
answerButton.addEventListener("click", () => {
answerContainer.innerHTML = `<li class="show-answer">${answer1}</li>`;
});
Now what I can't seem to manage is to hide the answer when the button is clicked again (the next challenge will be that the button will change the text to "Hide Answer" after the first click, but I have no idea how to approach that yet). The closest I got was this:
answerButton.addEventListener("click", () => {
answerContainer.innerHTML = `<li class="show-answer">${answer1}</li>`;
answerContainer.classList.toggle("hide-answer");
});
However, this method displays the .hide-answer class first, after which the 2 classes are toggled and everything is as it should be. So after the first click, the answer is still hidden and only after the 2nd click the button behaves the way I want it to.
I have tried this as well:
answerButton.addEventListener("click", () => {
answerContainer.innerHTML = `<li class="hide-answer">${answer1}</li>`;
answerContainer.classList.toggle("show-answer");
});
But for some reason this shows the container with all the CSS properties, but there is no text:
Answer Container is there, but no text
This is the CSS for the 2 classes (show-answer and hide-answer):
.show-answer {
background-color: hotpink;
border-radius: 7px;
border: none;
list-style: none;
width: 50%;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
box-shadow: rgba(0, 0, 0, 0.3) 0px 19px 38px;
}
.hide-answer {
display: none;
}
If anybody has any idea how I could get the result I need, I would be extremely grateful...
You're mixing up the answer-container with the answer-container's child (the innerHtml <li> element).
initially there's a visible, but empty <ul class="answer-container"></ul>.
Next on click of the button, you add the content into the answer-container expecting it to be visible with a show-answer class
Immediately after, you add the hide-answer class to the <ul class="answer-container"> parent element which hides the newly added content.
Click the button again and you finally see your answer because the container element has the hide-answer class toggled off. From here it works as you're expecting.
You can fix this by having the answer-container be hidden initially and then continue to toggle the display of the container. You can also just use a DOM element's hidden attribute to do this as I do in this code snippet below where I've taken your exact example and just modified the answer-container to start with hidden and toggle the hidden attribute on click. You can do the same thing w/ a CSS display: none class too.
const answerButton = document.querySelector(".answer-button");
const answerContainer = document.querySelector(".answer-container");
const answer1 = "Lorem ipsum dolor sit amet consectetur adipisicing elit.";
answerButton.addEventListener("click", () => {
answerContainer.innerHTML = `<li class="answer">${answer1}</li>`;
answerContainer.hidden = !answerContainer.hidden;
});
.answer {
background-color: hotpink;
border-radius: 7px;
border: none;
list-style: none;
width: 50%;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
box-shadow: rgba(0, 0, 0, 0.3) 0px 19px 38px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<section class="question-card">
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam vitae
labore repudiandae tenetur. Qui maiores animi quibusdam voluptatum
nobis. Nam aperiam voluptatum dolorem quia minima assumenda velit libero
saepe repellat. Tempore delectus deleniti libero aliquid rem velit illum
expedita nostrum quam optio maiores officiis consequatur ea, sint enim
cum repudiandae inventore ab nemo?
</p>
<div class="bookmark">
<i class="fa-regular fa-bookmark fa-lg"></i>
</div>
<button class="answer-button" data-js="answer-button">Show Answer</button>
<ul class="answer-container" hidden data-js="answer-container">
</ul>
<div class="container-categories">
<button class="category-button category-html">#html</button>
<button class="category-button category-flexbox">#flexbox</button>
<button class="category-button category-css">#css</button>
<button class="category-button category-js">#js</button>
</div>
</section>
Would something like this work?
You just use if the container has the class show-answer to determine if the answer needs to be shown or hidden
answerButton.addEventListener("click", () => {
if (answerContainer.classList.contains('show-answer')) {
// container has `showing` class
// hide the answer
answerContainer.innerHTML = ``; // ? - my guess, not sure how to want to hide it
}else{
// container doesn't have `showing` class
// show the answer
answerContainer.innerHTML = `<li class="hide-answer">${answer1}</li>`;
};
// update class
answerContainer.classList.toggle("show-answer");
});
Let's say that i have lots of text that i dont want to be in one single huge line, how could i put some white spacings after a certain amount of words? And should i do this in the CSS file or somewhere else?
So for example: this: "You have succesfully looted the house, as the house was empty you didnt run into any trouble." to: "You have succesfully looted the house, as the house was empty you
didnt run into any trouble."
Might not look as great on this site but i think you will get the idea. The string is empty at first and will be filled by some action that happens on the page.
Specify a width on the container where your text is inside. It is better to use the max-width property since your text may be smaller than the maximal width you want. In this case it is not essential though, since the paragraph is a block element and is full width.
p {
border: 1px solid lightcoral;
}
.ch-wrap {
max-width: 60ch;
}
.px-wrap {
max-width: 200px;
}
<h1>This has no width set</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam, doloremque nihil. Illo, veritatis est ipsa cumque culpa praesentium dolor error.</p>
<h1>This has a width of 60 characters</h1>
<p class="ch-wrap">Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam, doloremque nihil. Illo, veritatis est ipsa cumque culpa praesentium dolor error.</p>
<h1>This has a width of 200px set</h1>
<p class="px-wrap">Lorem ipsum dolor sit amet consectetur adipisicing elit. Laboriosam, doloremque nihil. Illo, veritatis est ipsa cumque culpa praesentium dolor error.</p>
You can use <br> tag to write in a new line. If you want to put white spaces you have to move the text via CSS. For example:
HTML:
<font id="moving">Some text</font>
CSS: #moving { float: right; }
but if it not works you can try with:
#moving { position: absolute !important; float: right; }
The !important attribute makes sure that the position tag will be set on absolute.
Is it possible in CSS (if not, javascript?) to shrink content that overflows its container, rather than hide it?
I have a box with some text etc, which shrinks in width as the viewport gets smaller, and the height is restricted too. All the content needs to remain visible, but within the bounds. Scroll is not an option.
body {
background-color: mediumaquamarine;
}
div {
width: 300px;
height: 150px;
overflow: hidden;
background-color: #fff;
}
<div>
<h1>example title</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Obcaecati fugiat dolore amet odit quaerat iusto sapiente ea quod atque necessitatibus id eius accusantium itaque voluptatibus laborum, doloremque, recusandae, nobis consequatur.</p>
<button>a button</button>
</div>
Hmm, the question is a little unclear, but you could look into using viewport width font-size. It will resize based on the screen size:
body {
background-color: mediumaquamarine;
}
div {
width: 300px;
height: 150px;
overflow: hidden;
background-color: #fff;
font-size: 1.3vw;
}
button {
font-size: 1.3vw;
}
<div>
<h1>example title</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Obcaecati fugiat dolore amet odit quaerat iusto sapiente ea quod atque necessitatibus id eius accusantium itaque voluptatibus laborum, doloremque, recusandae, nobis consequatur.</p>
<button>a button</button>
</div>
Use this function to adjust the font-size of a span inside a div:
function adjustHeights(elem) {
var fontstep = 2;
if ($(elem).height() > $(elem).parent().height() || $(elem).width() > $(elem).parent().width()) {
$(elem).css('font-size', (($(elem).css('font-size').substr(0, 2) - fontstep)) + 'px').css('line-height', (($(elem).css('font-size').substr(0, 2))) + 'px');
adjustHeights(elem);
}
}
div {
position: fixed;
width: 200px;
height: 100px;
border: 1px solid blue;
padding: 1px;
overflow-wrap: break-word;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span id="text">
sldfjslfjladf
sf
as
f
wer
qwreqwrewasdf
sd
f
s
fs
df
s
ffadsssssssssssssssssssssssssssss
asdf<br/>
sdf
s
dsfsfaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</span>
</div>
<script>
function adjustHeights(elem) {
var fontstep = 2;
if ($(elem).height() > $(elem).parent().height() || $(elem).width() > $(elem).parent().width()) {
$(elem).css('font-size', (($(elem).css('font-size').substr(0, 2) - fontstep)) + 'px').css('line-height', (($(elem).css('font-size').substr(0, 2))) + 'px');
adjustHeights(elem);
}
}
var div = document.getElementById("text");
adjustHeights(div);
</script>
JSFiddle: http://jsfiddle.net/e8B9j/1390/
SVG with text element.
Make an svg with expandable text.
It will eventually be hard to read.
<div style="width: 60px;">
<svg width="100%" height="100%" viewBox="0 -200 1000 300"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<text font-size="300" fill="black">Text</text>
</svg>
</div>
Non styled example
https://jsfiddle.net/k8L4xLLa/32/
Cool styled example
https://jsfiddle.net/k8L4xLLa/14/
You can use media queries to change the font size when needed.
Honestly though, consider flexbox. Its a much cleaner solution that does not make the text scalable. Then you can wrap your elements when theyre too small. :)
I have a table that has one row and it contains two tags.
Each td contains a div. Initially the first td (div) has a greater height. And the second td's div is centered vertically.
The user can push a button and new content will be added to the second td inside it's div. This will make the 2nd td's div have a greater height than the first td's. This will cause the 1st td's div to be then centered vertically.
How can I have both of these divs to be stretched vertically instead of centered?
This behavior is exactly what you get by default. All cells in a row must be the same height and by default that height is determined by the tallest content in any cell of a row.
document.querySelector("button").addEventListener("click", function(){
var cell1 = document.getElementsByTagName("td")[0];
var cell2 = document.getElementsByTagName("td")[1];
var temp = cell1.innerHTML;
cell1.innerHTML = cell2.innerHTML;
cell2.innerHTML = temp;
});
table, td { border:1px solid black; }
<table>
<tr>
<td>I'm taller<br>than the next cell.</td>
<td>I'm short</td>
</tr>
</table>
<button>Swap</button>
You can use jQuery to make it easy for the same td height which is maximum. LiveFiddle. Ask me any question in comment section.
jQuery(function(){
$('.btn').click(function() {
$('#added-text').append("<br>" + $('input[name="addtext"]').val());
});
//You Dont need this equal height function so you can remove this part
$("td").each(function() {
var maxHeight = 0;
if ($(this).height() > maxHeight) {
maxHeight = $(this).height();
}
$("td").height(maxHeight);
});
})
tr td {
vertical-align: top;
}
td:not(:last-child) {
color: red;
height: 50px;
}
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<table border="1">
<tr>
<td>
<div class="td_div">
Content 1
</div>
</td>
<td>
<div class="td_div" id="added-text">
Content 2
</div>
</td>
</tr>
</table>
<br>
<br>
<input type="text" name="addtext" placeholder="add some text" />
<button class="btn">
Click to add
</button>
After some research, I found one solution which worked in my case: set the table height to 1px and the cell height to 100%. I haven't heavily tested it yet though.
table {
height : 1px;
}
.cell {
height: 100%;
background: red;
}
<table>
<thead>
<tr>
<th> Column 1</th>
<th> Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="cell">Hello world</div>
</td>
<td>
<div class="cell">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto debitis corporis sapiente odit voluptatem aut, quae deleniti aperiam, nobis dolore dolorem quis ipsam accusantium omnis
explicabo quia doloremque aliquam eius magni, laudantium culpa eaque? Odio ipsam eius incidunt laborum animi accusamus obcaecati a deleniti neque, cupiditate quae iusto dolores exercitationem
unde aliquam dolore cumque quam nulla, minus dolor ipsum amet.
</div>
</td>
</tr>
</tbody>
</table>
I have started designing a template for my webpage using html and for first time.
I have used divs inside the body tag.Everything looks fine when the screen is maximized.
But if i minimize the screen, the alignment looks very odd.How can i design the webpage so that it fits the screen all the time.Here is my sample code.
<body height="100%">
<table width="100%" style="border-spacing: 0;">
<tr>
<td></td>
</tr>
</table>
<div>
<div style="float: left; position:relative;width:700px;height:75px;border:2px solid black;">
</div>
<div style="float: right; width:530px;height:260px;border:2px solid black;">
</div>
<div id="calendartable" style="float:right;position:relative;width:530px;height:30px;border:2px solid black;"></div>
</body>
You are talking something called responsive design Check this
It's very hard to understand what you actually want to accomplish.
First of all, only use for real tables, such as a timetable, or something you would use Exel for. We don't use tables in modern webdesign anymore. Really. :)
Secondly, you should try to come up with an idea of how your site should look like. Before you do any code at all.
A very simple site with "responsive" design:
<body>
<div id="main">
<header>
<nav>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
</ul>
</nav>
</header>
<div id="content" class="content">
<h1>My page title</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odit eligendi non quaerat tempora totam similique aliquid quas architecto rem ratione iure recusandae. Sit incidunt sint amet maxime necessitatibus expedita aspernatur?</p>
<h2>Subtitle</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ullam cupiditate similique nostrum impedit nulla doloremque assumenda quis provident ducimus nihil iusto veniam voluptatibus distinctio aperiam et vel quae ex libero!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt corrupti repellendus quibusdam praesentium cum facilis laboriosam numquam dolor atque cupiditate ullam quasi optio? Ratione maxime quam dolores sint dicta rerum.</p>
</div>
<footer class="footer">
<p>© 2014 – My Fancy Pagename.</p>
<p>Please do copysteal. You're free to use whatever you like from this page as long as you attribute me and link back.</p>
</footer>
</div>
(http://jsfiddle.net/n3gn5/)
As you can see here, I am not using any inline-CSS (that style="color:red;" stuff), but I have the CSS in an extra file:
body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, sans-serif;
}
nav ul li {
list-style: none;
display: inline;
}
nav ul li a {
text-decoration: none;
color: gray;
margin-right: 10px;
}
h1 {
font-size: 80px;
font-weight: 100;
color: orange;
}
footer {
font-size: 12px;
color: gray;
font-weight: 100;
}
#main {
width: 70%;
margin: auto;
}
With the jsfiddle link, you can mess around and see the result if you like.
I would really recommend to get some help with HTML, CSS and JavaScript. Codecademy does some really nice and free courses. On the other hand there are quite some good books out there. Just make sure with books or articles online: Pick some that are not older than a few years. If you pick up a book or tutorial from 1997, you'll learn wrong stuff, you'll learn bad habits and behavior. It will possibly work (table-layouts), but you won't have any fun with it, nor be able to create good websites with that knowledge.
If you're done there and have special questions, come back and ask them.
Your question feels a little like "How do I build a car? I have no idea!". There is no good answer to that kind of questions.
I hope I could help anyway. :)
you can use bootstrap to develop a responsive web page.
Else, you need to understand the rules about the responsive design. try to follow one of this example and understand how to use MediaQueries ;)
http://www.hongkiat.com/blog/responsive-web-tutorials/