I'm working on a tag scroller that will basically allow users to scroll through chunks of tags left or right if there are more tags than fit their current containing div. My plan was to have the component div set to overflow:hidden so the tags (and their parent div) would not wrap. Then I'd have left and right arrows that would animate the tag wrapper to the left or right.
I need to determine if the width of the tag-wrapper is greater than the tag-scroller itself. If so, then I know that there are more tags than fit within the tag-scroller and I should make the arrows visible so a user can click to scroll and view some more. The layout and everything looks as expected however my problem is, using $('.tag-wrapper').width(); always returns a different value depending on window width which shouldn't be the case since the actual content hasn't changed. If the screen is wide enough, I may not need to show the arrows so I need to check the width on window resize, etc.
Any ideas why $('.tag-wrapper').width(); would give me different sizes based on the actual window width even thought the scrollable content itself hasn't changed?
Here is my markup:
.tag-scroller {
overflow: hidden;
white-space: no-wrap;
display: block;
width: 100%;
height: 50px;
}
.tag-wrapper {
white-space: nowrap;
display: inline-block;
border: 1px solid green;
}
.tag {
display: inline-block;
width: initial;
text-align: center;
padding: 5px 10px 6px 10px;
}
<div class="tag-scroller">
<div class="tag-wrapper">
<div class="tag"></div>
<div class="tag"></div>
<div class="tag"></div>
<div class="tag"></div>
</div>
</div>
I used the jQuery outerWidth method.
$(function() {
$(document).on('DOMNodeInserted DOMNodeRemoved', '.tag-wrapper', function(e) {
$('#p').text('tag-wrapper width: ' + $('.tag-wrapper').outerWidth() + 'tag-scroller width: ' + $('.tag-scroller').outerWidth());
if ($('.tag-wrapper').outerWidth() > $('.tag-scroller').outerWidth()) {
alert('ok');
}
});
$('#btn').click(function(e) {
$('.tag-wrapper').append($('<div class="tag">1</div>'));
});
$('#btnWidth').click(function(e) {
$('#p').text('tag-wrapper width: ' + $('.tag-wrapper').outerWidth() + 'tag-scroller width: ' + $('.tag-scroller').outerWidth());
});
});
.tag-scroller {
overflow:hidden;
white-space:no-wrap;
display:block;
width:100%;
height:50px;
}
.tag-wrapper {
white-space: nowrap;
display:inline-block;
border:1px solid green;
}
.tag {
display:inline-block;
width:initial;
text-align:center;
padding: 5px 10px 6px 10px;
}
<script src="http://code.jquery.com/jquery-1.11.3.js"></script>
<div class="tag-scroller">
<div class="tag-wrapper">
<div class="tag">1</div>
<div class="tag">2</div>
<div class="tag">3</div>
<div class="tag">4</div>
</div>
</div>
<button id="btn">Click Me</button>
<button id="btnWidth">Check Widths</button>
<p id="p"></p>
Related
I am having problems with my JavaScript code. I am implementing a card game where I click a button and 13 cards are supposed to show up in intervals.
$("button").click(function() {
let i = 0;
setInterval(function() {
if(i == 4) clearInterval();
$(".block").eq(i).css({visibility:"visible"});
$(".block").eq(i).html("TEXT" + i);
i++;
},100);
});
.block {
display: inline-block;
width: 100px;
height: 140px;
border: 2px solid;
visibility: hidden;
}
button {
position: absolute;
top: 170px;
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<button id="button">Generate!</button>
</body>
</html>
As seen above, I use the setInterval() function to display them with 100ms intervals, all the divs do what I tell them to do but they first appear quite below where I want them to be. How can I make it so that they appear in the correct places directly?
Thanks in advance!
Add vertical-align: top; to your inline elements
$("button").click(function() {
let i = 0;
setInterval(function() {
if(i == 4) clearInterval();
$(".block").eq(i).css({visibility:"visible"});
$(".block").eq(i).html("TEXT" + i);
i++;
},100);
});
.block {
display: inline-block;
width: 100px;
height: 140px;
border: 2px solid;
visibility: hidden;
vertical-align: top;
}
button {
position: absolute;
top: 170px;
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<button id="button">Generate!</button>
</body>
</html>
Setting the blocks to display: none and then adding display: inline-block is a way of getting around the problem, but doesn't fix the problem itself.
The main issue is the vertical-align property set on the block class. By default, this is set to baseline. Before your button is clicked, all your divs are lined up in a row, invisible, with their baseline set to the bottom of the div. However, when the button is clicked, not only do your blocks become visible, but more crucially, you add some text inside the div. This changes the baseline, making it the bottom of the text within the div instead. However, because of vertical-align: baseline, the baselines of all the divs in the row try to align. The baseline of the visible divs with text has to align with the baseline of the invisible divs with no text. But their baselines are now different, so the only way they can all sit in a straight line on their baselines would be if the divs with text are pushed down.
I've simplified your snippets to show you what I mean. I've made the divs visible, removed the button, and instead, have manually added some text into your divs in html. As you can see, for the divs with text, the bottom of the text aligns with the bottom of the div without text.
body {
background: white;
}
.block {
display: inline-block;
width: 100px;
height: 140px;
border: 2px solid;
}
<html>
<body>
<div class="block">TEXT</div>
<div class="block">TEXT</div>
<div class="block"></div>
<div class="block">TEXT</div>
</body>
</html>
The reason why changing the blocks to display: none in the beginning, and then displaying them one by one works is because in this case, there is never a point when textless divs and divs with text are present in the DOM at the same time, so there is never a mismatch of baselines. The divs enter the DOM with text in them, and so their baselines always match up. However, this doesn't entirely fix the issue. If the text in the divs were of different lengths, for instance, the bottom of the multiline text would match up with the bottom of the single-line text, resulting in misalignment once again.
Example:
body {
background: white;
}
.block {
display: inline-block;
width: 100px;
height: 140px;
border: 2px solid;
}
<html>
<body>
<div class="block">text</div>
<div class="block">text</div>
<div class="block">very long text which takes up more than one line</div>
<div class="block">text</div>
</body>
</html>
So the proper fix for this would be to add vertical-align: top to the block class, to make sure that our alignment doesn't jump all over the place in response to the changing baseline.
You can set your .block element to display: none; instead of visibility: hidden; and change your script into this:
$("button").click(function() {
let i = 0;
setInterval(function() {
if(i == 4) clearInterval();
$(".block").eq(i).css({display:"inline-block"});
$(".block").eq(i).html("TEXT" + i);
i++;
},100);
});
Fiddle
You can put a wrapper div around the .block elements.
<html>
<body>
<div class="container">
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
</div>
<button id="button">Generate!</button>
</body>
</html>
And then the CSS:
.container {
display: flex;
}
.block {
/* display: inline-block; */
width: 100px;
height: 140px;
border: 2px solid;
visibility: hidden;
margin-left: 15px;
}
I added a margin-left to all the .block elements, but you can of course set them with the flex display or however you want.
Here is a working fiddle.
You could set the height to 0 then set it in the interval function.
$("button").click(function() {
let i = 0;
setInterval(function() {
if(i == 4) clearInterval();
$(".block").eq(i).css({visibility:"visible", height: "140px"});
$(".block").eq(i).html("TEXT" + i);
i++;
},100);
});
With this css:
.block {
display: inline-block;
width: 100px;
height: 0;
border: 2px solid;
visibility: hidden;
}
button {
position: absolute;
top: 170px;
left: 50px;
}
when I put a button inside the div id="box" then the button appears as a tiny rectangular box, whereas when i put it outside the div id="box" then it appears correctly with the proper text contained within it.
Please take a look on this link:
<style>#box {
border: yellow 5px solid;
background-color: black;
padding: 10px;
width: 300px;
}
#box img {
width:
}
#box button {
/* adjust the button width to fit nicely */
width: 10px;
;
height: 4px;
padding: 2px;
}
</style>
<script src="task1.js" type="text/javascript"></script>
<body>
<h1> Owais Ughratdar </h1>
<div id="box">
<img id="light" src="light_0.jpg">
<div>
<button> ON </button>
</div>
</div>
</body>
In your CSS file, try experimenting with #box button...need to make width and height much bigger like so:
#box button {
/* adjust the button width to fit nicely */
width: 300px;;
height: 200px;
padding: 2px;
}
https://jsfiddle.net/noLqau4x/
This is a change in Chrome 49 from previous versions. In 49, if I have a draggable element, when I drag it over a vertical scrollbar it causes the horizontal scrollbar to scroll to the right, even when it's not accepted.
$(document).ready(function() {
$("#dragItem").bind("dragstart", function(e) {
e.originalEvent.dataTransfer.setData("Text", 'data');
});
});
#dropContainer {
border: 1px solid black;
overflow-x: auto;
overflow-y: auto;
width: 250px;
height: 250px;
display: inline-block;
}
#bigContent {
width: 1000px;
height: 1000px;
}
#dragSourceContainer {
display: inline-block;
vertical-align: top;
margin-top: 20px;
}
#dragItem {
border: 1px solid black;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="dropContainer">
<div id="bigContent">
</div>
</div>
<div id="dragSourceContainer">
<div id="dragItem" draggable="true">
drag me to the left, over top the vertical scrollbar
</div>
</div>
JS Fiddle:
https://jsfiddle.net/a1qnbkeb/
My question is: how can I stop this behaviour without doing some crazy temporary scroll binding? The reason I ask this is because inside "dropContainer", I do allow things also to be dragged, and if they are dragged "within" drop container, I do want this behaviour.
I just don't want it when they're dragging new things in from outside.
I have a single container div with two child div's. The container div is 100% width. The child div's are left floated. The left div's width is not set because it's contents must decide it's width. The right div's width must be 100% minus the width of the left div.
HTML:
<div class="container">
<div class="message-name"><p>User :</p></div>
<div class="message-msg"><p>Some message</p></div>
</div>
<div class="container">
<div class="message-name"><p>User : </p></div>
<div class="message-msg"><p>Some really long message that breaks to new line because it is too long to stay on this line. mmmm mmmmmmmmmmmmm mmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmm mmmmmmmmm mmmmmmmmm mmmmmmmmmm mmmmmmmmmm mmmmmmmmm mmmmmmmm</p></div>
</div>
CSS:
*{margin:0;pading:0;}
.container{
width:100%;
min-height: 20px;
overflow: auto;
}
.message-name{
height: 20px;
text-align: left;
float: left;
border: 1px solid red;
}
.message-msg{
border: 1px solid red;
min-height: 20px;
float: left;
}
This is my attempt at using JQuery to dynamically set the width of the right div when it is added to the page dynamically:
$(document).ready( function(){
var nameWidth = $(".message-name").last().width();
alert(nameWidth);
$(".message-msg").last().css("width","100%").width($(".message-msg").last() - nameWidth);
});
But it doesn't change anything.
How can I get the width of the left div and then subtract that from the width of the right div to ensure the right div does not break to a new line?
Here is a JSFiddle of my attempt.
Use flexbox, it's support is wide enough for most reasonable purposes.
No scripting required, much more FLEXible!
* {
margin:0;
padding:0;
}
.container {
width:100%;
min-height: 20px;
overflow: auto;
display: flex;
flex-direction: row;
}
.message-name {
height: 20px;
text-align: left;
border: 1px solid red;
flex-shrink: 0;
}
.message-msg {
border: 1px solid red;
min-height: 20px;
flex-grow: 1;
flex-shrink: 1;
}
<div class="container">
<div class="message-name"><p>User :</p></div>
<div class="message-msg"><p>Some message</p></div>
</div>
<div class="container">
<div class="message-name"><p>User : </p></div>
<div class="message-msg"><p>Some really long message that breaks to new line because it is too long to stay on this line. mmmm mmmmmmmmmmmmm mmmmmmmmmmmmm mmmmmmmmmmm mmmmmmmmmm mmmmmmmmm mmmmmmmmm mmmmmmmmmm mmmmmmmmmm mmmmmmmmm mmmmmmmm</p></div>
</div>
Also on JSFiddle
A more efficient way to use flexbox is to just declare the .message-msg block to be flex: https://jsfiddle.net/84vocLbk/. It'll be situated horizontally next to the .message-name and stretch the available width.
CSS:
.message-msg {
border: 1px solid red;
min-height: 20px;
display: flex;
}
Please try this
$(".message-msg").last().width($(".message-msg").last().width() - nameWidth);
Border 2px for each div is present. If you want to place it to the left then try this
$(".message-msg").last().width($(".message-msg").last().width()-2 - nameWidth-2);
DEMO without removing border
DEMO after removing the border
Add this
$(".message-msg").last().css("width","100%").width($(".message-msg").last().width() - nameWidth);
In thaat line you're setting the width to 100% then changing that width to 100% minus the variable nameWidth You have to get width of last div to do calculations
You can achieve this with CSS.
fiddle: https://jsfiddle.net/zof15z6c/7/
This works by setting the overflow of the second div to hidden or auto. if your content is just a text I suggest setting it to hidden since the text would just wrap around.
Changes to the css
.message-msg{
border: 1px solid red;
min-height: 20px;
overflow:hidden;
}
Advantages:
Works on most browsers (tested in IE7)
The browser takes care of window resizes
Cleaner code
Disadvantages
Overflow should either be hidden or auto. This will not be an issue for you since you just have text.
I've got an element with overflow: hidden, which I'd like to expand when clicked.
This is what I have so far.
http://jsfiddle.net/up6bW/2/
It does expand the element, but not as it's supposed to. It should not push the element below it, but overlap and hide it. I can make this work partially by using position: absolute, but this makes the next element collapse to the top.
Can this be done by only using CSS on the clicked element? Other elements should not be adjusted.
Or if they are, this should be calculated automatically using JavaScript.
Another solution could also involve wrapping the div in a container like so:
HTML:
<div class="container">
<div class="a" onclick="z(this)">
click here click here click here click here click here
</div>
</div>
<div>1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
CSS:
body { margin: 10px; }
div { font-family: sans-serif; font-size: 16px; line-height: 20px; margin-bottom: 10px; width: 150px; word-break: break-all; }
div.a { color: tomato; cursor: pointer; height: 20px; overflow: hidden; }
.container { height: 20px; overflow: visible; }
JS:
function z (a) {
a.style.cssText = a.style.border ? "" : "\
background: #fff;\
border: 1px solid #ccc;\
height: auto;\
margin-left: -5px;\
margin-top: -5px;\
padding: 4px;\
position: absolute;\
";
};
DEMO HERE
Obviously adding HTML elements for presentational reasons is less than ideal, but I think it's better than a JavaScript alternative.
Tested in IE7+, Chrome, and Firefox
Here's an example of what you might need:
http://jsfiddle.net/up6bW/39/
All I did was make the position:absolute on your dropdown Div and then gave the first of the other divs a padding on top to compensate for the loss of space from the absolute positioning:
First you can change your second div a bit to add a class:
<div class="a" onclick="z(this)">click here click here click here click here click here</div>
<div class="second">1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
Then change the CSS to something like this:
body {
margin: 10px;
}
div {
width: 150px;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
margin-bottom: 10px;
word-break: break-all;
}
div.a {
cursor: pointer;
color: tomato;
height: 20px;
overflow: hidden;
position:absolute;
}
.second{
padding:25px 0px 0px 0px;
}
The div you want to expand will have absolute positioning then the second div will have enough padding to make up for that first div.
Placing elements on top of each other requires absolute positioning. You can put some padding-top on the first element to compensate for the positioning of the overlap.
I'm using this solution, which automatically adds padding to the next element.
http://jsfiddle.net/up6bW/47/
HTML
<div class="a" onclick="z(this)">click here click here click here</div>
<div>1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
<div class="a" onclick="z(this)">click here click here click here</div>
<div>1234567890</div>
<div>1234567890</div>
<div>1234567890</div>
<div class="a" onclick="z(this)">click here click here click here</div>
CSS
div {
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
width: 150px;
word-break: break-all;
}
div.a {
color: tomato;
cursor: pointer;
height: 20px;
overflow: hidden;
}
JavaScript
function z (a) {
// nextElementSibling equivalent
var b = a.nextSibling;
while (b && b.nodeType != 1)
b = b.nextSibling;
if (b)
b.style.cssText = b.style.paddingTop ? "" : "padding-top: " + a.clientHeight + "px";
a.style.cssText = a.style.border ? "" : "\
background: #fff;\
border: 1px solid #ccc;\
height: auto;\
margin-left: -5px;\
margin-top: -5px;\
padding: 4px;\
position: absolute;\
";
};