I am trying to make playing cards using CSS. I followed a YouTube video to make them and they were fine, but now I want to draw multiple cards next to each other, drawing 1 every time I click a button (I am trying to make Blackjack for a school project). The problem is that the cards will be placed under the previous one, and I have tried fiddling with it a bit and I think I have solved one of the issues but I have created another one. Let me show you.
Before, card-container had position absolute which is why i think was a problem, but now the bottom right number and suit is not even inside the container, also it still doesn't place the cards next to each other.
JavaScript that is executed when I click a button
I know that there is a bunch of stuff that there are other problems mainly in the JavaScript like the index of all getClassName, I know how to solve that, this is what I just cant figure out.
var cardcon = document.createElement('div');
cardcon.className = "card-container";
document.getElementById('gugu').appendChild(cardcon);
//
var card = document.createElement('div');
card.className = "card";
document.getElementsByClassName('card-container')[0].appendChild(card);
//
var valuecon = document.createElement('div');
valuecon.className = "value-container container-top";
document.getElementsByClassName('card')[0].appendChild(valuecon);
//
var valuenumber = document.createElement('div');
valuenumber.className = "value-number";
valuenumber.textContent = "7";
document.getElementsByClassName('value-container container-top')[0].appendChild(valuenumber);
//
var valuesuit = document.createElement("div");
valuesuit.className = "value-suit";
valuesuit.innerHTML = "♥";
document.getElementsByClassName('value-number')[0].appendChild(valuesuit);
//
var valuedon = document.createElement("div");
valuedon.className = "value-container container-bottom";
document.getElementsByClassName('card')[0].appendChild(valuedon);
//
var valuenumber1 = document.createElement('div');
valuenumber1.className = "value-number";
valuenumber1.textContent = "7";
document.getElementsByClassName('value-container container-bottom')[0].appendChild(valuenumber1);
//
var valuesuit1 = document.createElement("div");
valuesuit1.className = "value-suit";
valuesuit1.innerHTML = "♥";
document.getElementsByClassName('value-number')[1].appendChild(valuesuit1);
.card-container {
position: relative;
}
.card {
width: 250px;
height: 350px;
border: 3.5px solid gray;
border-radius: 12.5px;
box-shadow: 10px 10px grey;
background-color: white;
}
.value-container {
position: absolute;
}
.value-number {
font-family: 'Abel', sans-serif;
font-size: 30px;
}
.value-suit {
font-size: 30px;
}
.container-bottom {
bottom: 8px;
right: 16px;
transform: rotate(180deg);
}
.container-top {
top: 20px;
left: 20px;
}
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto;
grid-gap: 10px;
background-color: #2196F3;
padding: 10px;
}
<div id="gugu" class="grid-container"></div>
I have changed this part of your css to put down number inside your card
.card-container {width:250px;}
.card {width:100%;}
var cardcon = document.createElement('div');
cardcon.className = "card-container";
document.getElementById('gugu').appendChild(cardcon);
//
var card = document.createElement('div');
card.className = "card";
document.getElementsByClassName('card-container')[0].appendChild(card);
//
var valuecon = document.createElement('div');
valuecon.className = "value-container container-top";
document.getElementsByClassName('card')[0].appendChild(valuecon);
//
var valuenumber = document.createElement('div');
valuenumber.className = "value-number";
valuenumber.textContent = "7";
document.getElementsByClassName('value-container container-top')[0].appendChild(valuenumber);
//
var valuesuit = document.createElement("div");
valuesuit.className = "value-suit";
valuesuit.innerHTML = "♥";
document.getElementsByClassName('value-number')[0].appendChild(valuesuit);
//
var valuedon = document.createElement("div");
valuedon.className = "value-container container-bottom";
document.getElementsByClassName('card')[0].appendChild(valuedon);
//
var valuenumber1 = document.createElement('div');
valuenumber1.className = "value-number";
valuenumber1.textContent = "7";
document.getElementsByClassName('value-container container-bottom')[0].appendChild(valuenumber1);
//
var valuesuit1 = document.createElement("div");
valuesuit1.className = "value-suit";
valuesuit1.innerHTML = "♥";
document.getElementsByClassName('value-number')[1].appendChild(valuesuit1);
.card-container {
position: relative;
width:250px;
}
.card {
width:100%;
height:350px;
border:3.5px solid gray;
border-radius: 12.5px;
box-shadow: 10px 10px grey;
background-color: white;
}
.value-container {
position: absolute;
}
.value-number {
font-family: 'Abel', sans-serif;
font-size: 30px;
}
.value-suit{
font-size: 30px;
}
.container-bottom{
bottom: 8px;
right: 16px;
transform: rotate(180deg);
}
.container-top{
top:20px;
left:20px;
}
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto;
grid-gap: 10px;
background-color: #2196F3;
padding: 10px;
}
<div id="gugu" class="grid-container">
</div>
I'm not sure if I understand your problem but this is my solution.
to card-container add {display : flex}.
&& to card add {position : relative} (this is to fix the numbers floating out of the card)
Use an Inline-Block
<div> tags use by default the css property display:block;. This means that they will automatically claim the entire row that you place them on. The simplest solution is to use an object with the display:inline-block; property. Inline-blocks have all of the same other properties as a block element, but there is no automatic line-break that happens after it. So, if you got your code working right in all other respects using <div>s doing this will fix your problem without having to get messy trying to manipulate your position attributes at all.
To really simplify your code, just use <span> tags instead of <div> tags for your cards since these are inline-blocks by default.
Related
I am new to css. How can I add a status button which changes color depending on chat availability on top of another button?
You can use the position property.
See an example code here.
Some resources:
https://www.w3schools.com/css/css_positioning.asp
https://developer.mozilla.org/en-US/docs/Web/CSS/position
From the picture i can tell you don't have to use 2 html elements on top of each other, but you can use css properties like border and background-color to achieve exactly as the button in your picture.
I posted how in the code below with even a little bit of javascript to toogle the button status (not needed for styling, so if you don't know any javascript yet, you can skip that part).
let isOpen = false;
const btn = document.querySelector("#btn");
const dot = document.querySelector(".dot");
const txt = document.querySelector("#text");
btn.addEventListener("click", () => {
if (isOpen) {
dot.style.backgroundColor = "red";
txt.innerHTML = "The chat is now closed";
} else {
dot.style.backgroundColor = "green";
txt.innerHTML = "The chat is now open";
}
isOpen = !isOpen;
});
.dot {
height: 25px;
width: 25px;
background-color: red;
border-radius: 50%;
display: inline-block;
border: 5px solid gray;
}
#wrapper {
border: 1px solid black;
text-align: center;
padding: 10px;
}
#btn {
margin-top: 10px;
}
<div id="wrapper">
<span class="dot"></span>
<p id="text">The chat is now closed</p>
</div>
<button id="btn">Toogle</button>
I am trying to dynamically add video element to a div with each video element has 50vh width. So technically after ever 2 video elements, 3rd video element would be below to the left of screen.
I have tried:
.views-container {
padding: 0;
margin: 0;
font-size: 0;
}
.video-inset {
outline: unset;
position: relative;
margin:0;
padding:0;
}
<div class="views-container background-black" id="container"></div>
And JS
// Current condition is for 3 video elements but could be more
// height width added dynamically and could be different depending on number of elements
// 3 video elements in a row (33.33vw width) if greater than 15 video elements
const container = document.getElementById('container')
const video = document.createElement('video');
video.style.height = 50+"vh"
video.style.width = 50+"vw"
container.append(video);
It works fine only until 2 containers. Please help.
You can use display: grid; on the .views-container. I have used a width and height of 50vw and 50vh. You need to change the 2 (the number of columns) in repeat(2, 1fr) to another number.
const container = document.getElementById('container');
let n = 20; //Total number of videos
for (var i = 1; i <= n; i++) {
const video = document.createElement('video');
if(n > 15){
container.style.gridTemplateColumns = "repeat(3, 1fr)";
video.style.height = "33.33vh";
video.style.width = "33.33vw";
} else{
video.style.height = "50vh";
video.style.width = "50vw";
}
video.src = "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
container.append(video);
}
.views-container {
padding: 0;
margin: 0;
font-size: 0;
display: grid;
grid-template-columns: repeat(2, 1fr)
}
.video-inset {
outline: unset;
position: relative;
margin: 0;
padding: 0;
}
<div class="views-container background-black" id="container"></div>
How to make a div appear on click
You can achieve this by using event listeners on mouse click and appending images to the div element like below code snippet:
const addImageDiv = document.getElementsByClassName('add-image')[0];
addImageDiv.addEventListener('click', (event) => {
const elem = document.createElement("img");
addImageDiv.appendChild(elem);
elem.src = 'https://australiahouse.us/sticker-1.png';
elem.setAttribute('style', `left: ${event.clientX}px; top: ${event.clientY}px; width: 25vw;`);
});
.add-image {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
border: 1px solid lightgrey;
}
body img {
pointer-events: none;
position: absolute;
transform: translate(-50%,-50%) scale(.35);
}
<div class="add-image">
<span>some content</span>
</div>
EDIT:
It's a bit trickier to produce a random image of a small set of images, but the best practical way I suggest to create an array of images, then produce a random number and after that get the floor of random number and array length to produce a random index, and at last you can get a random image on each click. (keep in mind in this method, in a small set of images you have to try several times to get a new image on click!)
const addImageDiv = document.getElementsByClassName('add-image')[0];
const imagesArray = ['https://australiahouse.us/sticker-1.png', 'https://australiahouse.us/sticker-2.png', 'https://australiahouse.us/sticker-3.png'];
const imagesArrayLength = imagesArray.length;
addImageDiv.addEventListener('click', (event) => {
const randomNumber = Math.random();
const randomIndex = Math.floor(randomNumber * imagesArrayLength);
const elem = document.createElement("img");
addImageDiv.appendChild(elem);
elem.src = imagesArray[randomIndex];
elem.setAttribute('style', `left: ${event.clientX}px; top: ${event.clientY}px; width: 25vw;`);
});
.add-image {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
border: 1px solid lightgrey;
}
body img {
pointer-events: none;
position: absolute;
transform: translate(-50%,-50%) scale(.35);
}
<div class="add-image">
<span>some content</span>
</div>
Essentially, I'm looking to animate a line on the left and right side of text that will increase its width to the end of the display when I hover over the text.
Perhaps this will help...
without hovering:
SOME TEXT
on hover:
----------------------------SOME TEXT--------------------------------------------------------------------------------------
I'd like these lines to animate outward to the end on the parent. I've tried using the pseudo elements but had no luck. Some help would be greatly appreciated.
Here's how I'd do it. Feel free to play with animation duration and timing function:
.separator {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.separator:before, .separator:after {
content: '';
flex-grow:0;
height: 1px;
background-color: currentColor;
transition: flex-grow .6s cubic-bezier(.4,0,.2,1);
margin: 0 .5rem;
}
.separator:hover:before, .separator:hover:after {
flex-grow: 1;
}
<div class="separator">SOME TEXT</div>
<div style="width: 50%; margin-top: 60px;border: 1px solid red; color: blue; padding: 3rem 0;">
<div class="separator">TEST</div>
Here's a JavaScript solution. Adds - to either side until it reaches the end of the line, and it removes the dashes when the mouse moves away.
To prevent the overflow, you just have to track the clientHeight and stop adding dashes as soon as the height increases.
var div = document.getElementsByClassName('test')[0];
var origText = div.innerText;
var origHeight = div.clientHeight;
var tooLong = false;
var addTxtInt;
div.addEventListener('mouseover', function() {
addTxtInt = setInterval(function() {
if (tooLong)
return;
if (div.clientHeight > origHeight) {
div.innerText = div.innerText.substring(1, div.innerText.length - 1);
tooLong = true;
return;
}
div.innerText = "-" + div.innerText + "-";
if (div.clientHeight > origHeight) {
div.innerText = div.innerText.substring(1, div.innerText.length - 1);
tooLong = true;
return;
}
}, 80);
});
div.addEventListener('mouseleave', function() {
clearInterval(addTxtInt);
div.innerText = origText;
tooLong = false;
});
.test {
display: block;
overflow: hidden;
text-align: center;
}
<div class='test'>SOME TEXT</div>
I'm trying to make an upvote/downvote the same way that it's done on SO and Reddit, from what I can see they use arrow images as backgrounds and then position it, but I'm a CSS newbie and I need someone to walk me through it.
You could do it by adding a different picture to the background, one for every state of the button. There is however a cleaner, easier, more modern way of achieving this result: Sprites.
A sprite is an image that is saved as a part of a larger image. One of the biggest advantages of using sprites is the reduction of round-trips to the server for all the images to just one request for the Sprites. The element to display a picture has the image as background. The background is moved relative to the element so the element displays only part of the image. Like when you move a photo-frame over a poster (or in this case: moving the poster under the frame)
At SO they make an image that contains all the states for the button. They give the element for the button (a span in this case) a fixed width and height and add the background to it with CSS. Then toggle a class for the state (on or off) with javascript on the click event. Now the only thing you have to do in CSS is change the position of the background with CSS classes:
for (const btn of document.querySelectorAll('.vote')) {
btn.addEventListener('click', event => {
event.currentTarget.classList.toggle('on');
});
}
.vote {
display: inline-block;
overflow: hidden;
width: 40px;
height: 25px;
cursor: pointer;
background: url('http://i.stack.imgur.com/iqN2k.png');
background-position: 0 -25px;
}
.vote.on {
background-position: 0 2px;
}
Click to vote (using sprites): <span class="sprite vote"> </span>
You can easily add more states to the sprites like 'hover' and 'active' just the same way. SO even puts all the images for the whole page in a single image. You can verify this with firebug or the Chrome developer tools. Look for 'sprites.png'.
Update (2020)
It's been 10 years since I answered this question and in this time,
the landscape has changed. Now you can use inline svg as well to achieve this effect. I've updated the code snippet to use svg. This is how stackoverflow currently does this.
It works by toggling the color property of a surrounding span element on button click. The span element contains an inline svg image of an arrow. The fill property of the path that makes up the arrow is initialized with currentColor, which instructs it to take whatever is the current text color.
for (const btn of document.querySelectorAll('.vote')) {
btn.addEventListener('click', event => {
event.currentTarget.classList.toggle('on');
});
}
.vote {
display: inline-block;
cursor: pointer;
color: #687074
}
.vote.on {
color: #f48024
}
Click to vote (using svg):
<span class="vote">
<svg width="36" height="36">
<path d="M2 10h32L18 26 2 10z" fill="currentColor"></path>
</svg>
</span>
You can do it by using two simple images ... design two images in some image editors like Photoshop, if u don't have MSPaint...
CSS code is
#voting{
width:30px;
height:40px;
}
.upvote{
width:30px;
height: 20px;
cursor: pointer;
}
.downvote{
width:30px;
height: 20px;
background: url('downvote.jpg') 0 0 no-repeat;
cursor: pointer;
}
HTML code :
<div id="voting">
<div class="upvote"></div>
<div class="downvote"></div>
</div>
I'm doing project on django, and I'm trying to implement up-vote and down-vote on many posts, I've taken #Jan's code partly and finished it.
vote.html
<span onclick="like_function({{user_answer.pk}})" id="like-{{user_answer.pk}}" class="vote_up_off"></span>
<div id="counter-{{user_answer.pk}}">0</div>
<span onclick="dislike_function({{user_answer.pk}})" id="dislike-{{user_answer.pk}}" class="vote_down_off"></span>
vote.css
/* like dislike button */
.vote_up_off {
display: inline-block;
overflow: hidden;
width: 40px;
height: 25px;
cursor: pointer;
background: url(' https://i.stack.imgur.com/nxBdX.png');
background-position: 0 -25px;
margin-left: 5px;
}
.vote_up_on {
background-position: 0 2px;
display: inline-block;
overflow: hidden;
width: 40px;
height: 25px;
cursor: pointer;
background: url('https://i.stack.imgur.com/nxBdX.png');
margin-left: 5px;
}
.vote_down_off {
display: inline-block;
overflow: hidden;
width: 40px;
height: 25px;
cursor: pointer;
background: url('https://i.stack.imgur.com/vWw7n.png');
background-position: 0 -1px;
margin-top: 3px;
}
.vote_down_on {
display: inline-block;
overflow: hidden;
width: 40px;
height: 25px;
cursor: pointer;
background: url('https://i.stack.imgur.com/vWw7n.png');
background-position: 0 -28px;
margin-top: 3px;
}
vote.js
function like_function(answer_id) {
var like_button = document.getElementById('like-'+answer_id);
var dislike_button = document.getElementById('dislike-'+answer_id);
var counter_element = document.getElementById('counter-'+answer_id);
let current_counter = parseInt(counter_element.innerText);
//check if dislike is on(true) or off(false)
let dislike_state = false
if (dislike_button.className == "vote_down_on") {
dislike_state = true
}
else {
dislike_state = false
}
//if dislike is checked
if (dislike_state) {
current_counter += 2;
dislike_button.className = 'vote_down_off'
counter_element.innerText = current_counter
like_button.className = 'vote_up_on'
}
// if dislike is not checked
else {
if (like_button.className == 'vote_up_off') {
like_button.className = "vote_up_on"
current_counter += 1;
counter_element.innerText = current_counter
}
else {
like_button.className = "vote_up_off"
current_counter += -1;
counter_element.innerText = current_counter
}
}
}
function dislike_function(answer_id) {
var like_button = document.getElementById('like-'+answer_id);
var dislike_button = document.getElementById('dislike-'+answer_id);
var counter_element = document.getElementById('counter-'+answer_id);
let current_counter = parseInt(counter_element.innerText);
//check if like is on(true) or off(false)
let like_state = false
if (like_button.className == "vote_up_on") {
like_state = true
}
else {
like_state = false
}
//if like is checked
if (like_state) {
console.log('это тру лайк (лайк нажат)')
current_counter += -2;
like_button.className = 'vote_up_off'
counter_element.innerText = current_counter
dislike_button.className = "vote_down_on"
}
//if like is not checked
else {
if (dislike_button.className == 'vote_down_off') {
dislike_button.className = "vote_down_on"
current_counter += -1;
counter_element.innerText = current_counter
}
else {
dislike_button.className = "vote_down_off"
current_counter += 1;
counter_element.innerText = current_counter
}
}
}