I'm creating a selector plugin (which can set active class to the multiple elements).
Algorithm:
onMouseDown create element;
onMouseMove while onMouseDown move to elements I want to select, my created element changes size.
onMouseUp get event target and do magic.
This is the short version due to shortness but I have a problem with getting proper event target. When I move to the right and bottom/top (my x axis gets positive), I'm getting the proper event target. But when my x-axis is negative I always getting e.target the element which changes size itself, not the elements I need.
const container = document.querySelector('.container');
let selector = null;
container.addEventListener('mousedown', (e) => {
selector = document.createElement('div');
selector.style.position = 'fixed';
selector.style.border = '1px dashed blue';
selector.style.width = '1px';
selector.style.height = '1px';
selector.style.left = e.clientX+'px'
selector.style.top = e.clientY+'px'
selector.className = "selector";
container.appendChild(selector);
const mouseDownClientX = e.clientX;
const mouseDownClientY = e.clientY;
document.onmousemove = e => {
renderselector(e, mouseDownClientX, mouseDownClientY);
}
document.onmouseup = e => {
console.log(e.target);
selector.remove();
}
});
function renderselector(e, mouseDownClientX, mouseDownClientY) {
const top = Math.min(e.clientY, mouseDownClientY);
const left = Math.min(e.clientX, mouseDownClientX)
const width = Math.abs(e.clientX - mouseDownClientX);
const height = Math.abs(e.clientY - mouseDownClientY);
selector.style.width = width + 'px';
selector.style.height = height + 'px';
selector.style.top = top+'px';
selector.style.left = left+'px';
}
* {
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
.container {
display: flex;
justify-content: center;
align-content: center;
flex-wrap: wrap;
width: 50vw;
height: 100vh;
margin: 0 auto;
border: 1px solid gray;
}
.item {
padding: 20px;
flex: 0 0 50px;
border: 1px solid gray;
}
<div class='container'>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Needed to change z-index of my selector to lesser than my divs.
Related
This question already has answers here:
Can't scroll to top of flex item that is overflowing container
(12 answers)
Closed 1 year ago.
I have a column flexbox with width:100% and overflow-x:auto , inside this flexbox there are variable numbers of rows and each row can contain 1,2,4,8,16,... elements.
consider bellow example:
first row ---> has 1 element
second row ---> has 2 elements
third row ---> has 4 elements
forth row ---> has 8 elements
...
and elements on each row should be centered.
in my code all the left side elements will be cut off , how can I prevent this ??
const itemWidth = 300; //300px
document.querySelectorAll('.item').forEach((item) => {
item.style.width = `${itemWidth}px`;
});
document.querySelectorAll('.row').forEach((row, i) => {
row.style.width = `${
(Math.pow(2, i) + (Math.pow(2, i) - 1)) * itemWidth
}px`; //calc needed width of each row
});
.rows {
width: 100%;
overflow-x: auto;
display: flex;
flex-direction: column;
align-items: center;
}
.row {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.item {
height: 100px;
background-color: royalblue;
margin: 20px;
}
<div class="rows">
<div class="row">
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
For the first div. You are assigning a width of only one item. So, flex items got cut off to left. Please check the below code. Get the length of items contains a max length of a row. Assign it to all the divs. If your unable to view the below code in snippet. Check it here - https://codepen.io/kcsmastermind/pen/MWpPBrY
const itemWidth = 300; //300px
document.querySelectorAll('.item').forEach((item) => {
item.style.width = `${itemWidth}px`;
});
let rows = document.getElementsByClassName("row");
let maxNumber = 0;
for (let i=0; i < rows.length; i++) {
if (maxNumber < rows[i].childElementCount) maxNumber = rows[i].childElementCount;
};
document.querySelectorAll('.row').forEach((row, i) => {
row.style.minWidth = `${
maxNumber * itemWidth
}px`; //calc needed width of each row
});
.rows {
display: flex;
flex-direction: column;
align-content: center;
width: 100%;
overflow-x: auto;
}
.row {
display: flex;
justify-content: center;
margin-top: 20px;
}
.item {
height: 100px;
background-color: royalblue;
margin: 20px;
}
<div class="rows">
<div class="row">
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
Here's a pure CSS solution (I took some liberties with styling to hopefully make it easier to see what's going on):
https://jsfiddle.net/5hq924uf/1/
Rather than calculating element widths via Javascript, the .rows container width is set based on its child content with this rule:
width: min-content;
This then props the page open, preventing elements from getting cut off when centered using flex.
Note that while the min-content feature seems to be well-supported in modern browsers, it's not going to work in some legacy browsers, so be aware:
https://caniuse.com/mdn-css_properties_width_min-content
I have a container that can be scrolled through horizontally. This was done by rotating the container by 90 degrees.
This is however only a visual workaround: the visual horizontal scrolling is done by physical vertical scrolling. On mobile devices this is very counter-intuitive.
Is there any way to manipulate the scrolling behaviour so that the container can be scrolled through with horizontal touch gestures?
Here is a simplified demonstration of the container:
.item {
background: black;
height: 50px;
width: 50px;
margin: 5px;
}
.container {
background: yellow;
width: fit-content;
height: 300px;
overflow-y: scroll;
transform: rotate(-90deg);
transform-origin: top right;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Instead of using transform, actualy place the elements side by side in a flex container.
Because you don't want the elements to shrink with the container size you a can set a min-width on the items:
.item {
background: black;
height: 50px;
width: 50px;
margin: 5px;
min-width:50px;
}
.container {
display:flex;
background: yellow;
width: 300px;
overflow-x: scroll;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
I have two div elements:
When a user scrolls div #element-A and #header-one-target reaches the top of the containing div the last element (#animate-hd-b) in #element-B should scroll to the top of the containing div with a nice animation .
Here's the code that I'm working with to start. The code below does something when the window is scrolled not the div.
$(window).scroll(function() {
var offsetTop = $('#animate-hd-b').offset().top,
outerHeight = $('#animate-hd-b').outerHeight(),
windowHeight = $(window).height(),
scrollTop = $(this).scrollTop();
console.log((offsetTop-windowHeight) , scrollTop);
if (scrollTop > (offsetTop+outerHeight-windowHeight)){
alert('you have scrolled to the top!');
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="element-A" style="background: orange; overflow: auto;">
<div class="content" style="padding-bottom: 300px;">
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<h1 id="header-one-target">Header One</h1>
</div>
</div>
<div id="element-B" style="background: yellow; overflow: auto;">
<div class="content" style="padding-bottom: 300px;">
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<h1 id="animate-hd-b">Animate This Header</h1>
</div>
</div>
Is there a way to do this in jQuery?
This is really pretty simple. You just keep track of #header-one-target and animate
#animate-hd-b when #header-one-target reaches at the top.
(function($) {
let $elementA = $('#element-A');
let $elementB = $('#element-B');
let $headerOneTarget = $('#header-one-target');
let $animateHdB = $('#animate-hd-b');
let isScrollAtTop = true;
$elementA.scroll(function() {
if (isScrollAtTop && $headerOneTarget.offset().top < 5) {
isScrollAtTop = false;
$elementB.animate({
scrollTop: $elementB.scrollTop() + $animateHdB.offset().top
});
} else if ($elementA.scrollTop() < 5) {
isScrollAtTop = true;
$elementB.animate({
scrollTop: 0
});
}
});
})(jQuery);
#element-A {
background: orange;
overflow: auto;
height: 100vh;
width: 60vw;
position: fixed;
top: 0;
left: 0;
}
#element-B {
position: fixed;
top: 0;
right: 0;
height: 100vh;
width: 40vw;
background: yellow;
overflow: auto;
}
.content {
padding: 10px;
}
.content-vh100 {
height: 100vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="element-A">
<div class="content">
<p>Scroll</p>
<p>to</p>
<p>header</p>
<p>one</p>
<h1 id="header-one-target">Header One</h1>
<div class="content-vh100"></div>
</div>
</div>
<div id="element-B">
<div class="content">
<p>to</p>
<p>animate</p>
<p>following</p>
<p>content</p>
<h1 id="animate-hd-b">Animate This Header</h1>
<div class="content-vh100"></div>
</div>
</div>
Some conditions were added that should prevent unnecessary animations and queueing (which tends to happen when listening for scroll and animating scrollTop). It keeps track of the scroll direction and won't start animating when the element on the right has already reached its position.
Codepen demo
var sin = $('#element-A'),
dex = $('#element-B'),
peg = sin.scrollTop();
sin.scroll(function() {
var way = sin.scrollTop(),
rate = Math.round(sin.find('h1').position().top),
area = dex.scrollTop(),
turf = Math.round(dex.find('h1').position().top),
down = way > peg;
peg = way;
// conditions for scrolling down
if (rate < 0 && down && turf) {
dex.not(':animated').animate({scrollTop: area+turf}, 700);
}
// scrolling up
if (!down && area) {
dex.not(':animated').animate({scrollTop: 0}, 700);
}
});
body {
margin: 0;
}
body > div {
width: 50%;
height: 100vh;
float: left;
overflow: auto;
}
#element-A {
background: orange;
}
#element-B {
background: yellow;
}
.content {
padding-bottom: 100vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="element-A">
<div class="content">
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<h1 id="header-one-target">Header One</h1>
</div>
</div>
<div id="element-B">
<div class="content">
<p>content</p>
<p>content</p>
<p>content</p>
<p>content</p>
<h1 id="animate-hd-b">Animate This Header</h1>
</div>
</div>
In case the elements are differently positioned in the target environment, using position() is a more straightforward approach than offset() because the latter is relative to the document. The former (used here) is relative to its own parent element and should work independent of its position.
Example code:
Html:
<div class="wrap">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="wrap">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="wrap">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
The css:
*{
margin: 0;
padding: 0;
}
.wrap{
width: 30%;
height: 200px;
background: #f7f7f7;
float: left;
}
.wrap:not(:first-child){
margin-left: 5%;
}
.item{
width: 100%;
height: 47px;
margin-bottom: 3px;
background: #999;
}
The js code that works:
(function($){
$('.wrap').each(function(){
var thisWrap = $(this),
naturalHeight = thisWrap[0].scrollHeight,
restrictedHeight = thisWrap[0].clientHeight;
if (naturalHeight - 5 > restrictedHeight){
thisWrap.css({'background': 'red'});
}
});
})(jQuery);
The js code that doesn't:
(function($){
function myCustomFunction(){
$('.wrap').each(function(){
var thisWrap = $(this),
naturalHeight = thisWrap[0].scrollHeight,
restrictedHeight = thisWrap[0].clientHeight;
if (naturalHeight - 5 > restrictedHeight){
thisWrap.css({'background': 'red'});
}
});
}
$(window).resize(function(){
myCustomFunction();
});
myCustomFunction();
})(jQuery);
In this codepen example both work, but on my test page when the code is in a function and invoked after the definition of the function it doesn't work.
The wrap element is already on the page when the js loads, the js code detects for overflow, that css is defined in a file that gets loaded on line 39, the js gets loaded on line 1300+.
Putting all in a document ready function doesn't help.
How can this code in the same file work properly when not in a function?
EDIT: The issue seems to be with the window resize function, it seems that something is triggering that while the page loads.
This issue was caused by another script which was putting display: none; styling on the wrap-s(so the problem was it fired too late). I solved it by adding a setTimeout with 400ms on the tab change click, and including the function there.
To fix your problem use this
$(window).on('resize', function(){
myCustomFunction();
});
I am practicing CSS with Masonry. I'm doing a bit of basic testing and seeing how everything gets displayed. However I am not sure how to fix this problem. I want to fit all of my pictures inside the .masonry class in my CSS but there is this little bit of space that occurs and I'm not sure how to fix it. I want all the boxes to be flush and no white space to be displayed between each box.
Here is the codepen:
http://codepen.io/anon/pen/Cpedg
my CSS:
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body { font-family: sans-serif; }
.masonry{
margin: 0 auto;
background: #EEE;
max-width: 80%;
}
.masonry .item,
.masonry .grid-sizer {
width: 20%; /*item default width of 20%*/
margin: 0px;
}
.masonry .item,
.masonry .grid-sizer {
height: 60px; /* default hieght of image*/
float: left;
background: #D26;
border: 1px solid #343;
border-color: hsla(0, 0%, 0%, 0.5);
border-radius: 5px;
}
.item.w2 { width: 40%; } /* changes each item to these chracteristics*/
.item.h2 { height: 100px; }
.item.h3 { height: 130px; }
.item.h4 { height: 180px; }
my HTML:
<h1>Masonry - columnWidth</h1>
<div class="masonry js-masonry" data-masonry-options='{ "columnWidth": ".grid-sizer", "itemSelector": ".item" }'>
<div class="grid-sizer"></div>
<div class="item"></div>
<div class="item w2 h2"></div>
<div class="item h3"></div>
<div class="item h2"></div>
<div class="item w3"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item w2 h3"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item w2 h2"></div>
<div class="item w2"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h3"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h2"></div>
</div>
You can remove those spaces between the cells by adding (or rather removing) margin and padding to (from) your body, like so:
body {
font-family: sans-serif;
margin: 0;
padding: 0;
}
That's a possible solution for the upper left arrow, but I'm not quite sure what the problem with the second arrow is?
So it turns out google chrome's zoom acts a bit funky sometimes. quick fix to realign everything is to press ctrl + 0 to realign everything!