I am building a website that expands horizontally as user takes action like http://portal.azure.com style. When they click a button(from a list) in one div, the details of the selected items appear in another div beside it. this can get really long and over flow the mother div.
I am looking for a way i can automatically scroll the page to the right most edge when a new div overflows.
layout
<div style="overflow-x: auto">
<div layout="row">
<div class="col" style="width: 400px">
</div>
//SHOWN DYNAMICALLY
<div class="col" style="width: 400px">
</div>
//SHOWN DYNAMICALLY
<div class="col" style="width: 400px">
</div>
//SHOWN DYNAMICALLY
<div class="col" style="width: 400px">
</div>
//SHOWN DYNAMICALLY
<div class="col" style="width: 400px">
</div>
</div>
</div>
As you can see above, the first div shows by default but the other divs appear based on user interaction.
By the time the 3 div appears, it overflows.
How can i scroll to the right edge anytime it over flows? (you should really check out http://portal.azure.com to see what im talking about)
PS: i am using AngularJS. I am not using jquery. But i dont mind including it if its the only option
You can use plain Javascript for keeping the scroll to right.
Something like this:
var myDiv = document.getElementById("row");
myDiv.scrollLeft = myDiv.scrollWidth;
You need to fire the above function every time you add a new div. That way it will always automatically be scrolled when divs are dynamically added.
You will need to hook up the DOMNodeInserted event on your container. The function will be called whenever a div is added to your row container. This way you will not have to change anything in your existing code.
Here is a very simple example with dynamically added divs:
var num = 1,
btn = document.getElementById('btn'),
row = document.getElementById("row");
scroller(); // fire teh scroller right away for initial scroll
// function to keep it scrolled towards right
// function scroller() { row.scrollLeft = row.scrollWidth; }
// edited to add simple animation
function scroller() {
var maxScroll = row.scrollWidth - row.clientWidth; // required to stop
row.scrollLeft += 2;
if (row.scrollLeft < maxScroll) {
timer = window.setTimeout(scroller, 1000 / 60);
}
}
// hook up event to call scroller whenever an element is dynamically added
row.addEventListener("DOMNodeInserted", scroller);
// for demo to simluate dynamically adding divs
btn.addEventListener("click", function() {
var newDiv = document.createElement("div");
newDiv.setAttribute("class", "col");
num += 1; newDiv.innerText = num;
row.appendChild(newDiv);
});
div[layout] {
width: 500px; height: 140px; white-space: nowrap;
overflow: hidden; overflow-x: auto;
}
div.col { height: 140px; width: 400px; display: inline-block; text-align:center; }
div { border: 1px solid red; }
<div id="row" layout="row"><div class="col">1</div></div>
<button id="btn">Add</button>
Edit: Added simple animation using setTimeout (in order to keep jQuery away). Ideally you should be using requestAnimationFrame or a suitable library if you are already using one.
Related
I've made a timeline using a sort of following this: https://www.w3schools.com/howto/howto_css_timeline.asp and set it to position: sticky. This is in a container called, div.timeContainer. Next to it, there's some text in a separate div. The idea is that the user scrolls down, reading the text on the right, while the timeline/overview on the left is in view.
The problem right now is that if I set the height of div.timeContainer, resizing the window means that the timeline will stop being in view/sticky around half-way through since the div on the right has become longer.
This (and variations) is what I have tried so far:
const historyContainer = document.querySelector("div.history").style.height
document.querySelector("div.timeContainer").style.height = historyContainer
I have prepared for you a simple example of assigning parent height to a child. An example in vanilla js.
let parent_div = document.querySelector('.parent');
let child_div = document.querySelector('.child');
let click_button = document.querySelector('input');
click_button.onclick = function(){
child_div.style.height = parent_div.offsetHeight + 'px';
};
.parent {
width: 100%;
height: 300px;
background-color: yellow;
}
.child {
width: 50%;
background-color: green;
}
<input type="button" value="click me to get the height of the child div">
<div class="parent">
<div class="child">
</div>
</div>
I'm trying to make a toggle which works, but every element I click on creates a stack of these showed elements. Instead I'm trying to hide everything and display only element that I clicked on. Now I can only hide it when I click on the same element twice, which is not what I want. I want to click on one and hide previous ones that were showing.
.totalpoll-choice-image-2 is a bunch of images that always has to be shown. They are what the user clicks on to display hidden description under each image. That description shows up when I click on .totalpoll-choice-image-2. There are 5 images with that class. The next image I click on, I want to hide the previous description box.
My code:
jQuery(document).ready(function() {
var element = document.getElementsByClassName("totalpoll-choice-image-2");
var elements = Array.prototype.slice.call(Array.from( element ) );
console.log(elements);
jQuery(element).each(function(item) {
jQuery(this).unbind('click').click(function(e) {
e.stopPropagation();
var id = jQuery(this).attr("data-id");
console.log(this);
//jQuery("#" + id).css({"display": 'block !important'});
//document.getElementById(id).style.setProperty( 'display', 'block', 'important' );
var descriptionContainer = document.getElementById(id);
var thiss = jQuery(this);
console.log(thiss);
console.log(jQuery(descriptionContainer).not(thiss).hide());
jQuery(descriptionContainer).toggleClass("show");
});
})
})
You can attach event handlers to a group of DOM elements at once with jQuery. So in this case, mixing vanilla JS with jQuery isn't doing you any favors - though it is possible.
I threw together this little example of what it sounds like you're going for.
The script itself is very simple (shown below). The classes and IDs are different, but the idea should be the same:
// Assign click handlers to all items at once
$('.img').click(function(e){
// Turn off all the texts
$('.stuff').hide();
// Show the one you want
$('#' + $(e.target).data('id')).show();
})
https://codepen.io/meltingchocolate/pen/NyzKMp
You may also note that I extracted the ID from the data-id attribute using the .data() method, and attached the event listener with the .click() method. This is the typical way to apply event handlers across a group of jQuery objects.
From what I understood based on your comments you want to show only description of image that has been clicked.
Here is my solution
$('.container').on('click', 'img', function() {
$(this).closest('.container').find('.image-description').addClass('hidden');
$(this).siblings('p').removeClass('hidden');
});
https://jsfiddle.net/rtsj6r41/
Also please mind your jquery version, because unbind() is deprecated since 3.0
You can use event delegation so that you only add your event handler once to the parent of your images. This is usually the best method for keeping work the browser has to do down. Adding and removing classes is a clean method for show and hide, because you can see what is happening by looking at your html along with other benefits like being easily able to check if an item is visible with .hasClass().
jsfiddle: https://jsfiddle.net/0yL5zuab/17/
EXAMPLE HTML
< div class="main" >
<div class="image-parent">
<div class="image">
</div>
<div class="image-descr">
Some text. Some text. Some text.
</div>
</div>
<div class="image-parent">
<div class="image">
</div>
<div class="image-descr">
Some text. Some text. Some text.
</div>
</div>
<div class="image-parent">
<div class="image">
</div>
<div class="image-descr">
Some text. Some text. Some text.
</div>
</div>
<div class="clear">
</div>
</div>
EXAMPLE CSS
.image-parent{
height: 100px;
width: 200px;
float: left;
margin: 5px;
}
.image-parent .image{
background: blue;
height: 50%;
width: 100%;
}
.image-descr{
display: none;
height: 50%;
width: 100%;
}
.show-descr{
display: block;
}
.clear{
clear: both;
}
EXAMPLE JQUERY
$(".main").on("click", ".image-parent", ShowDescription);
function ShowDescription(e) {
var $parent = $(e.target).parent(".image-parent");
var $desc = $parent.find(".image-descr");
$(".image-descr").removeClass("show-descr");
$desc.addClass("show-descr");
}
I'm building an AngularJS (among other things) powered site with a table in it, which is a bit too wide to display on devices with smaller screens.
Therefore I made it horizontally scrollable when the parent element, the page's main content block, is too narrow to fit the table div.
HTML:
<fieldset ng-show="tels.length > 0">
<div class="table-wrapper">
<div class="table" ng-repeat="type in types">
<div class="form-group control-group"
ng-repeat="tel in tels | filter:{type:type.name} | orderBy:'label'">
<div>{{tel.label}}</div>
<div>{{tel.value}}</div>
<div>{{tel.description}}</div>
...
</div>
</div>
</div>
</fieldset>
CSS:
.table-wrapper {
margin-bottom: 10px;
overflow-x: auto;
}
.table-wrapper .table {
table-layout: fixed;
min-width: 500px;
padding-right: 12px;
margin-bottom: 15px;
}
.table-wrapper .table > div {
margin-right: 0px;
margin-left: 0px;
}
This works, but because the scrollbar isn't always visible, and there's not always some cut-off content to hint at the table being scrollable, I want to add some indicators dynamically when it's possible to scroll the table, like this or this (but then horizontally)
To monitor the table's scroll position, I added a watcher to my angular code:
$scope.$watch($('.table-wrapper'), function() {
var scrolled = angular.element('.table-wrapper').scrollLeft();
$log.info("scrolled: " + scrolled);
});
This does seem to log the table's horizontal position, but only initially:
scrolled: 0
It doesn't update when I scroll the table, even though the DOM Element's scrollLeft property does reflect its position when inspected.
Strangely, the following jQuery code does log the horizontal scroll position upon scrolling, and I can get it to work as in this JsFiddle (reduce the size of the output screen):
$(document).ready(function() {
$('.table-wrapper').on('scroll', function() {
var scrolled = $('.table-wrapper').scrollLeft();
console.log("scrolled: " + scrolled);
});
});
No such luck in AngularJS.
What am I doing wrong?
below code will not work in this case,
try using events for scrollbar or use bootstrap with table-responsive class
$scope.$watch($('.scroll-horizontal'), function() {
var scrolled = angular.element('.scroll-horizontal').scrollLeft();
$log.info("scrolled: " + scrolled);
});
try using something like this
function logScroll(ev){
if(window.pageYOffset>400)alert('User has scrolled at least 400 px!');
}
window.onscroll=logScroll
I have a set of seven div's with the following properties:
height: 100px;
width: 100px;
display: inline-block;
I have a wrapper div containing these seven blocks with only enough room to fit four and change.
The overflow is hidden.
How can I make this function so that when you clicked and dragged horizontally, or swiped with your finger on mobile, the entire row of div blocks would slide to show the previously hidden ones?
Please refer to this jsFiddle for the example.
We can use css or jQuery here.
*Bonus, show fractions of otherwise entirely hidden div's at the edges of the container.
Based on jfriend00's answer I modified this so it will work on touch/click and move with the mouse.
var last_x = null;
var holding = false;
//Mark the wrapper as clicked/touched
$('.wrapper').mousedown(function(){
holding=true;
});
//We do this on document so that even if movement goes outside of the container the event will fire
$(document).mouseup(function(){
holding=false;
});
$('.wrapper').mousemove(function(e){
if(last_x === null || !holding) //If this is the first movement
{
last_x = e.pageX;
return;
}
var ammount = e.pageX - last_x;
$('.slider',this).css('margin-left', '+=' + ammount);
last_x = e.pageX;
});
The gist of how this works is that when the mousedown event is detected on the container the script starts tracking all mouse movement and moves the content with the mouse. When the mouse is released it stop tracking movement.
Fiddle: http://jsfiddle.net/NvJam/2/
Since no one has mentioned jQuery.Kinetic I'll add this:
<div class="carousel">
<div class="wrapper">
<div class="first">First</div>
<div class="second">Second</div>
<div class="third">Third</div>
<div class="fourth">Fourth</div>
<div class="fifth">Fifth</div>
<div class="sixth">Sixth</div>
<div class="seventh">Seventh</div>
</div>
</div>
$('.carousel').kinetic();
Demo: http://jsfiddle.net/louisbros/2pRBg/6/
see here
.wrapper {
width: 900px;
height: 100px;
overflow: hidden;
}
You can put an additional container div and use absolute positioning on that div to move the items left/right. Here's a demo:
http://jsfiddle.net/jfriend00/7edc9/
HTML looks like this:
<div class="wrapper">
<div class="slider">
<div class="first">First</div>
<div class="second">Second</div>
<div class="third">Third</div>
<div class="fourth">Fourth</div>
<div class="fifth">Fifth</div>
<div class="sixth">Sixth</div>
<div class="seventh">Seventh</div>
</div>
</div>
You weren't entirely clear how you wanted to move them on non-touch screens, but here's some event handlers that work on buttons:
$("#left").click(function() {
$(".slider").stop(true, true).animate({left: "-=125px"}, 500);
});
$("#right").click(function() {
$(".slider").stop(true, true).animate({left: "+=125px"}, 500);
});
Something similar could be hooked up for touch events.
Even better solution: use the JQuery UI draggable:
$('.slider').draggable({
axis: 'x',
});
http://jsfiddle.net/DCuGV/2/
I have a script that has a div with a width larger than its' parent, with the parent being set to overflow: hidden;. I have javascript that is setting the left positioning of the big div to create "pages". You can click a link to move between pages.
All of that works great, but the problem is if you tab from one "page" element to another, it completely messes up all the left positioning to move between the pages.
You can recreate this bug in the fiddle I set up by setting your focus to one of the input boxes on page ONE and tabbing until it takes you to page two.
I've set up a demo here.
The code that is important is as follows:
HTML:
<div class="form">
<div class="pagesContainer">
<div class="page" class="active">
<h2>Page One</h2>
[... Page 1 Content here...]
</div>
<div class="page">
<h2>Page Two</h2>
[... Page Content here...]
</div>
</div>
CSS:
.form {
width: 400px;
position: relative;
overflow: hidden;
border: 1px solid #000;
float: left;
}
.pagesContainer {
position: relative; /*Width set to 10,000 px in js
}
.form .page {
width: 400px;
float: left;
}
JS:
slidePage: function(page, direction, currentPage) {
if (direction == 'next') {
var animationDirection = '-=';
if (page.index() >= this.numPages) {
return false;
}
}
else if (direction == 'previous') {
var animationDirection = '+=';
if (page.index() < 0) {
return false;
}
}
//Get page height
var height = page.height();
this.heightElement.animate({
height: height
}, 600);
//Clear active page
this.page.removeClass('active');
this.page.eq(page.index()).addClass('active');
//Locate the exact page to skip to
var slideWidth = page.outerWidth(true) * this.difference(this.currentPage.index(), page.index());
this.container.animate({
left: animationDirection + slideWidth
}, 600);
this.currentPage = page;
}
The primary problem is that whatever happens when you tab from say, an input box on page one to something on page 2, it takes you there, but css still considers you to be at left: 0px;. I've been looking all over for a solution but so far all google has revealed to me is how to stop scrollbar scrolling.
Any help or suggestions would be appreciated, thanks!
P.S. The html was set up like this so that if javascript is disabled it will still show up all on one page and still function properly.
I updated your fiddle with a fix for the first tab with the form: http://jsfiddle.net/E7u9X/1/
. Basically, what you can do is to focus on the first "tabbable" element in a tab after the last one gets blurred, like so:
$('.form input').last().blur(function(){
$('.form input').first().focus();
});
(This is just an example, the first active element could be any other element)
Elements with overflow: hidden still have scrolling, just no scroll bars. This can be useful at times and annoying at others. This is why your position left is at zero, but your view of the element has changed. Set scrollLeft to zero when you change "pages", should do the trick.