Javascript check for empty spaces when css float:left - javascript

Okay now.. I really didn't know how to give this one a title. Here is my problem. I am trying to make a page where the body has a class defining the width of another div a.k.a baser.
I have a jQuery script that detects the width of the window and gives body a class hereafter. for example when the width of the window is about 900px - body will be given a class .col_6.
Each card is 150px in width, but when they have the additional class large they will be 300px in width. The problem now is that depending on the order of the cards in HTML, there will be some empty spaces if a card.large should be placed in a space only 150px wide. Then the card will automatically jump to next row and leave an open space of 150px in the previous row.
What I would like to know is - what can I add to my script to perhaps selecting a 150px wide card placed later in the order and move it in the empty spot or filling out the empty spot with a javascript made div ( for other purposes )..
I hope you are able to figure out my problem by reading this.. I have made a jsfiddle that shows my code and the problem. You can try resizing the window when reading the fiddle. It will show you how an empty space will be seen sometimes.
http://jsfiddle.net/EK8q2/4/
here is my javascript/jQuery if it can help you any way..
$(document).ready(function() {
winResize(window.innerWidth);
});
$(window).resize(function() {
winResize(window.innerWidth);
// Room for more functions
});
function winResize(w) {
resizeBaser(w);
// Room for more functions
}
function resizeBaser(w) {
var index = Math.min(10, Math.floor(w / 150));
if (index > 0) {
$('body').alterClass('col_*', 'col_' + index);
}
}

The algorithm as I see it is this:
Attach to load/scroll/resize events:
$(window).on('load scroll resize', function () {
});
There, group the $('.card') elements into an array of rows.
Something like:
var rowsObj = {}, rows = [];
$('.card').each(function(){
var el = $(this),
pos = el.position(),
currentRow = rowsObj[pos.top] || (rowsObj[pos.top] = []);
currentRow.push(el);
});
$.each(rowsObj, function(k, v) {
if (/^\d+(\.\d+)?$/.test(k)) {
rows.push(v);
}
});
// rows now contains array of arrays of $-ed elements
3. Then I'd enumerate the rows from end to start and tried to id all orphan cards (condition: single most narrow card) - having these, I'd search from top to bottom and tried to locate the possible new row (i.e. the last element of that potential row) and orphan.insertAfter(thatElement). Upon insertion, I'd exclude both rows (one containing the orphan, the other - incomplete row) from the array. Do until no pairs (orphan-incomplete) can be formed.
4.Then I'd think on how to extend this logic to widths larger than single card.

Related

Target CSS and JS to specific tables

I have three tables on a page, there are some responsive features I want applied to 2 of the 3 tables- the 3rd is just a basic 2 column table. The problem I am having is that the css + js is also applying globally to all tables including the third table which I more or less just need as basic without any js bells and whistles.
I have tried applying a class to the table and looking over the script but was unable to fix the issue.
https://codepen.io/GideonB/pen/rgWgPm I recreated the issue in this pen so that you can see as its a bit extensive. Basically the third smaller table has two columns but once the screen size is reduced below 1024px it reduces to one column (inheriting the styling I want applied only to the two larger tables) while hiding the other. Below is the script I am using.
$('ul').on('click', 'li', function() {
var pos = $(this).index() + 2;
$('tr').find('td:not(:eq(0))').hide();
$('td:nth-child(' + pos + ')').css('display', 'table-cell');
$('tr').find('th:not(:eq(0))').hide();
$('li').removeClass('active');
$(this).addClass('active');
});
// Initialize
var mediaQuery = window.matchMedia('(min-width: 640px)');
// Add Event Listener
mediaQuery.addListener(selectElement);
// Function Event Listener
function selectElement(mediaQuery) {
if (mediaQuery.matches) {
$('.sep').attr('colspan', 5);
} else {
$('.sep').attr('colspan', 2);
}
}
// On Load
selectElement(mediaQuery);
Any and all help and suggestions would be greatly appreciated. Basically I just can't seem to figure out how to target the two tables specifically and leave the third alone. Any changes I make to the two upper tables effects the smaller one.
Try adding this to your css outside of your media query, worked for me see code pen below:
table.si-cov-tb td{
display: table-cell;
}
codepen

Prevent viewport from moving when removing DOM elements

I'm trying to implement an HTML infinite scroller in which at any given time there are only a handful of div elements on list (to keep the memory footprint small).
I append a new div element to the list and at the same time I'm removing the first one, so the total count of divs remains the same.
Unfortunately the viewport doesn't stay still but instead it jumps backwards a little bit (the height of the removed div actually).
Is there a way to keep the viewport still while removing divs from the list?
I made a small self contained HTML page (well, it still needs JQuery 3.4.1) which exposes the problem: it starts by adding 5 divs and then it keeps adding a new one and removing the first one every 1 second
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function removeit() {
// remove first one
var tiles = $(".tile");
$(tiles[0]).remove();
}
function addit() {
// append new one
var jqueryTextElem = $('<div class="tile" style="height:100px;background-color:' + getRandomColor() + '"></div>');
$("#inner-wrap").append(jqueryTextElem);
}
function loop() {
removeit();
addit();
window.setTimeout(loop, 1000);
}
addit();
addit();
addit();
addit();
addit();
loop();
<div id="inner-wrap"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
You can temporarily add position: fixed to the parent element:
first add position: fixed to the parent;
then remove the item;
then remove position: fixed from the parent
I have a feeling you're trying to have your cake and eat it, in that if you get the viewport to be "still", I think you're meaning you don't want a user to see the scrollbar move and then also not have any new affordance to scroll further down the page, because you would want the scrollbar thumb/grabber to still sit at the bottom of the scrollbar track?
I mean, you could just use $(window).scrollTop($(window).scrollTop() + 100); in your example to make it so the scroll position of the viewport won't visually move when removing elements, but at that point, you wouldn't be keeping the users view of the current elements the same or even allowing a user to have new content further down the page to scroll towards. You'd just be "pushing up" content through the view of the user?
If you are trying to lighten the load of what is currently parsed into document because you are doing some heavy lifting on the document object at runtime, maybe you still want to remove earlier elements, but retain their geometry with some empty sentinel element that always has the height of all previously removed elements added to it? This would allow you to both have a somewhat smaller footprint (though not layout-wise), while still having a usable scrollbar that can communicate to a user and both allow a user to scroll down, towards the content that has been added in.
All in all, I think what you currently have is how most infinite scrollers do and should work, meaning the scroll position and scrollbar should change when content is added in the direction the user is scrolling towards, this communicates to them that they can in fact keep scrolling that way. You really shouldn't want the viewports scroll position to be "still".
To see more clearly why I don't think you have an actual issue, replace your loop() definition with something like this...
function loop() {
$(window).scroll(function() {
// check for reaching bottom of scroller
if ($(window).scrollTop() == ($(document).height() - $(window).height())) {
addit();
removeit();
}
})
}

Make scrollspy spy on other tags than headers

Just learning html, css and javascript and got this crazy idea of using the <abbr> tag in my code along with a scrollspy.js (from bootstrap) setup that spy on the viewed paragraphs and displays the definition of the abbreviations/acronym.
As I just learned the fundamental of javascript I want to know if that that possible by the script itself or does it need some modification (if so please post a code example, thx!)
If you look at the source of scrollspy.js you actually don't see any reference to headers. It works by looking at the navigation links and corresponding elements for clues about the relationship between content elements and that navigation. Here is how it looks in the documentation
For nav
#fat
For the section
<h4 id="fat">#fat</h4>
Now one thing to consider, <h4> elements are block level so they take up an entire row. Inline elements can be on the same line, so it might be impossible to see which definition should be highlighted based on the visible section. For what you are trying to do I think you need to create your own bit of JavaScript.
Here is one approach (though it will need some tweaking to work for your exact needs it supports inline abbr elements and even more than one definition per line per line) (and here is a working demo)
Here is the JavaScript:
var mapTerms = function () {
/*grab all of the <abbr>s with terms associated with them, they can move anytime the browser is resized*/
var terms = [];
$('abbr[data-term]').each(function () {
var term = $(this);
terms.push({
term: term.data('term'),
height: term.offset().top,
visible: false,
element: term
});
});
return terms;
},
terms = [],
/*Keep a globally accessible list*/
$body = $('body'); /*need a reference to the body for performance*/
$(window).on('scroll', function () {
/*listen to the scroll event*/
var height = $(document).scrollTop(),
i;
for (i = 0; i < terms.length; i++) {
if (terms[i].visible) {
if (terms[i].height < height || terms[i].height > (height + 50)) {
terms[i].visible = false;
terms[i].element.removeClass('defined');
$('div[data-term=' + terms[i].term + ']').hide();
}
} else {
if (terms[i].height > height && terms[i].height < (height + 50)) {
terms[i].visible = true;
terms[i].element.addClass('defined');
$('div[data-term=' + terms[i].term + ']').show();
}
}
}
}).on('resize', function () {
/*listen to the resize event*/
console.log('mapping...');
terms = mapTerms();
}).trigger('resize').trigger('scroll');
And here is some CSS that hides and highlights the terms and their definitions (as well as keeps the definitions visible):
abbr.defined {
background: red;
}
.definition {
display: none;
}
#definitions {
position: fixed;
}
The way it works is by looking for <abbr> that have a data element that points to a full definition like this:
<abbr data-term='blog' title='Webblog'>blog</abbr>
Each of these points to a div that holds a definition (stuffed in the sidebar at the bottom in the example)
<div class='definition' data-term='blog'>
<h4>Blog</h4>
<p>A blog (a truncation of the expression weblog) is a discussion or informational site published on the World Wide Web and consisting of discrete entries ("posts") typically displayed in reverse chronological order (the most recent post appears first).</p>
<p><i>source <a href='http://en.wikipedia.org/wiki/Blog'>Wikipedia</a></i></p>
</div>
Then as you resize the browser the onResize code builds a map of all of the <abbr> elements are, and as you scroll the onScroll code looks are your current position and tries to figure out if each term is visible (it is using a very simple definition of visible which is within the top 50 pixels of your browser). In production you would probably want to test if it is between the scroll top and the window height, or account for things like the user scrolling down to read more of the definition but pushing the term off the screen. It's a tricky thing to get 100% right, but this should get you further.

So DOM scrollIntoView aligns top/bottom, but what about left/right?

I would like to scroll horizontally the given element. The only way I find is using ScrollIntoView DOM method, which allows either align the element's bottom with the view bottom or the top - with the view top.
But what if the element is OK with respect to the Y axis, and I only want to scroll it horizontally? How can I align its left with the view left or its right with the view right?
EDIT
Here is more context. I have a YUI table with a horizontal scrollbar. I wish to scroll it programmatically to a certain TD node. I do not think window.scrollTo is of any help to me, since the scrollbar is on a div element, not on the whole page.
EDIT2
Turns out there is a duplicate SO question with the right answer - How can I scroll programmatically a div with its own scrollbars?
Voting to close mine.
I've recently had a problem with a table header that had inputs as filters for each column. Tabbing through the filters would move the focus, but if one of the inputs wasn't visible or if it was partly visible, it would only JUST bring the input into view, and I was asked to bring the full input and column into view. And then I was asked to do the same if tabbing backwards to the left.
This link helped to get me started: http://www.webdeveloper.com/forum/showthread.php?197612-scrollIntoView-horizontal
The short answer is that you want to use:
document.getElementById('myElement').scrollLeft = 50;
or:
$('#myElement')[0].scrollLeft = 50;
Here's my solution (which may be overkill for this question, but maybe it'll help someone):
// I used $.on() because the table was re-created every time the data was refreshed
// #tableWrapper is the div that limits the size of the viewable table
// don't ask me why I had to move the head head AND the body, they were in 2 different tables & divs, I didn't make the page
$('#someParentDiv').on('focus', '#tableWrapper input', function () {
var tableWidth = $('#tableWrapper')[0].offsetWidth;
var cellOffset = $(this).parent()[0].offsetLeft;
var cellWidth = $(this).parent()[0].offsetWidth;
var cellTotalOffset = cellOffset + cellWidth;
// if cell is cut off on the right
if (cellTotalOffset > tableWidth) {
var difference = cellTotalOffset - tableWidth;
$('#tableWrapper').find('.dataTables_scrollHead')[0].scrollLeft = difference;
$('#tableWrapper').find('.dataTables_scrollBody')[0].scrollLeft = difference;
}
// if cell is cut off on the left
else if ($('#tableWrapper').find('.dataTables_scrollHead')[0].scrollLeft > cellOffset) {
$('#tableWrapper').find('.dataTables_scrollHead')[0].scrollLeft = cellOffset;
$('#tableWrapper').find('.dataTables_scrollBody')[0].scrollLeft = cellOffset;
}
});
This is something I used a while back. There might be better and more efficient way of doing it but this gets the work done:
$(".item").click(function(event){
event.preventDefault();
// I broke it down into variable to make it easier to read
var tgt_full = this.href,
parts = tgt_full.split("#"),
tgt_clean = parts[1],
tgt_offset = $("#"+tgt_clean).offset(),
tgt_left = tgt_offset.left;
$('html, body').animate({scrollLeft:tgt_left}, 1000, "easeInOutExpo");
})
You just need to make sure that the item is an a tag with an href that corresponds to the target id element:
HTML:
go to section 1
...
<section id="one"> Section 1</section>
Hope this helps!

Stretch div as last resort

I am trying to create css to solve a problem with displaying a like button next to a page title. I have 3 cases.
The title fits to the left of the like button
The title is long and flows to multiple lines to the left of the button
The title has long words that overflow the div and cover the like button
My question: Is there a way to set up the div so when case 3 happens, the div grows and pushes the like button below the title but keeps the same functionality for cases 1 and 2?
Dynamically remove the width when the word causes overflow. By removing width, you cause the button to move down and thus behave like options 1&2.
Check these divs dynamically with some jQuery/JavaScript to see if a word causes an overflow.
//Javascript function to remove width class when overflow occurs.
//40em is an arbitrary width
$.each($('.ConstrainedDiv'),function() {
var wordregex = /\w+/g;
var content = $(this).html();
var wordArray = content.match(wordregex );
for(var i = 0; i < wordArray.length; i++) {
if(wordArray[i].length > 40) {
$(this).removeClass('someWidth');//behavior changes if you remove this
break;
}
}
});
Fiddle. Note the difference in behavior by commenting out the marked line. Note2 resolution and fiddle box width matter.[Demo tested in FF7]

Categories

Resources