How to reset jQuery after on click action - html

I'm creating a simple survey using jQuery where if user rates 3 stars or below, one div is opened. But if user rating is 4 or 5 stars, another div opens.
Everything works fine if you make a direct rating, for example, you select 3 stars and click "Submit".
However, if you select 3 stars, but then change your mind and select 4 stars, both div's are shown.
It's like the browser is remembering my last choice. Is there any way to reset behavior after initial selection?
I have a full example of my code here at CodePen https://codepen.io/mario-smode/pen/YzqdzMv
(function($) {
$('#stars li').on('mouseover', function(){
var onStar = parseInt($(this).data('value'), 10); //
$(this).parent().children('li.star').each(function(e){
if (e < onStar) {
$(this).addClass('hover');
}
else {
$(this).removeClass('hover');
}
});
}).on('mouseout', function(){
$(this).parent().children('li.star').each(function(e){
$(this).removeClass('hover');
});
});
/* 2. Action to perform on click */
$('#stars li').on('click', function(){
var onStar = parseInt($(this).data('value'), 10); // The star currently selected
var stars = $(this).parent().children('li.star');
for (i = 0; i < stars.length; i++) {
$(stars[i]).removeClass('selected');
}
for (i = 0; i < onStar; i++) {
$(stars[i]).addClass('selected');
}
});
$('.choice').on('click', function () {
var $this = $(this);
$('.reaction').removeClass('reaction-fade-in');
$('.emoji').removeClass('emoji-selected');
$this.find('.reaction').addClass('reaction-fade-in');
$this.find('.emoji').addClass('emoji-selected');
});
$('#question-1 .choice').on('click', function() {
var ranking1 = $(this).attr('data-value');
$('.answer-submit').on('click', function() {
sessionStorage.setItem('ranking1', ranking1);
$('#question-1').addClass('hide');
$('.summary').removeClass('hide');
var total;
function calculateTotal() {
var ranking1 = sessionStorage.getItem('ranking1');
var total = parseInt(ranking1) + 0;
if (total >= 4 ) {
$('.amazing').removeClass('hide');
} else if ((total < 4) && (total = 3 )) {
$('#terrible').removeClass('hide');
} else {
$('#terrible').removeClass('hide');
}
}
calculateTotal();
});
});
})( jQuery );
.leave-review-btn {
display: block;
background-color: #de531e;
width: 240px;
padding: 10px 20px;
border-radius: 25px;
color: #fff;
margin: 0 auto;
}
.review-left, .review-right {
display: block;
width: 50%;
float: left;
border: 1px solid #e8e8e8;
padding-bottom: 15px;
margin-bottom: 10px;
}
.review-logo {
padding-top: 40px;
}
.review-logo svg {
max-width: 200px;
height: 100px;
}
h3 {
font-weight: 300;
}
.question h1 {
text-align: center;
}
.em, .em-svg {
height: 3.5em !important;
width: 3.5em !important;
}
.button {
padding: 1rem;
color: white;
background: black;
text-decoration: none;
text-transform: uppercase;
display: block;
margin: 0 auto;
width: fit-content;
}
.hide {
display: none;
}
.emoji-choices {
list-style: none;
text-align: center;
padding: 0px 0 23px 0em !important;
}
#media (max-width: 899px) {
li.choice {
list-style: none;
}
.emoji-choices {
display: flex;
margin-top: 40px;
}
.review-left, .review-right {
display: block;
width: 100%;
float: left;
border: 1px solid #e8e8e8;
padding-bottom: 15px;
margin-bottom: 10px;
}
}
#media (min-width: 900px) {
.emoji-choices {
display: -webkit-box;
display: flex;
max-width: 500px;
margin: auto;
}
}
#media (min-width: 900px) {
.choice {
width: 30vw;
margin-top: 3rem;
list-style: none;
}
}
.emoji {
font-size: 1.5rem;
-webkit-transition: font-size 300ms ease;
transition: font-size 300ms ease;
cursor: pointer;
margin: 0;
}
.emoji-selected {
font-size: 2rem;
}
.reaction {
font-size: 1rem;
opacity: 0;
}
.reaction-fade-in {
-webkit-animation: fadeIn 300ms ease;
animation: fadeIn 300ms ease;
opacity: 1;
}
#-webkit-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.question {
margin-top: 1rem;
}
.summary {
text-align: center;
margin: auto;
max-width: 800px;
}
/* stars - remove to use smiley */
.rating-stars ul > li.star {
display: inline-block;
}
/* Idle State of the stars */
.rating-stars ul > li.star > p > i.fa {
font-size: 2.5em; /* Change the size of the stars */
color: #ccc; /* Color on idle state */
}
/* Hover state of the stars */
.rating-stars ul > li.star.hover > p > i.fa {
color: #ffcc36;
}
/* Selected state of the stars */
.rating-stars ul > li.star.selected > p > i.fa {
color: #ff912c;
}
.rating-stars ul {
list-style-type: none;
padding: 0;
-moz-user-select: none;
-webkit-user-select: none;
}
.rating-stars fa {
font-size: 2.5em;
color: #ccc;
}
<link href="https://afeld.github.io/emoji-css/emoji.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css" integrity="sha512-i8+QythOYyQke6XbStjt9T4yQHhhM+9Y9yTY1fOxoDQwsQpKMEpIoSQZ8mVomtnVCf9PBvoQDnKl06gGOOD19Q==" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<!-- Q1-->
<div id="question-1">
<div class="question rating-stars text-center">
<h1>How was your overall experience?</h1>
</div>
<div class="rating-stars text-center">
<ul class="emoji-choices" id="stars">
<li class="choice star" data-value="1">
<p class="emoji"><i class='fa fa-star fa-fw'></i></p>
<p class="reaction">I can't believe I wasted money on this!</p>
</li>
<li class="choice star" data-value="2">
<p class="emoji"><i class='fa fa-star fa-fw'></i></p>
<p class="reaction">I had such high hopes, but ultimately it was disappointing</p>
</li>
<li class="choice star" data-value="3">
<p class="emoji"><i class='fa fa-star fa-fw'></i></p>
<p class="reaction">Meh. I could take it or leave it</p>
</li>
<li class="choice star" data-value="4">
<p class="emoji"><i class='fa fa-star fa-fw'></i></p>
<p class="reaction">Thanks! I really enjoyed it.</p>
</li>
<li class="choice star" data-value="5">
<p class="emoji"><i class='fa fa-star fa-fw'></i></p>
<p class="reaction">I can't wait to tell everyone I know about this!</p>
</li>
</ul>
</div>
<a class="answer-submit button" href="#">Submit Answer</a>
</div>
<!-- Thank you-->
<div class="summary hide">
<h2>Thanks for taking our survey! Your feedback helps us make better products.</h2>
<div class="hide amazing">
<h3>We are SO happy that you enjoyed your experience with our product! Please share it with everyone you know!</h3>
<div class="review-logo">
<div class="review-left">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="266" height="100" viewBox="0 0 266 100" overflow="visible" enable-background="new 0 0 266 100" xml:space="preserve">
<g>
<rect fill="#3B5998" width="266" height="100"/>
</g>
<g>
<path fill="#FFFFFF" d="M140.0205078,49.5966797c-1.7841797,0-3.0703125,0.5849609-4.3740234,1.1811523v13.4858398 c1.2490234,0.1191406,1.9648438,0.1191406,3.1503906,0.1191406c4.2822266,0,4.8691406-1.9609375,4.8691406-4.6992188v-6.4414062 C143.6660156,51.2207031,142.9951172,49.5966797,140.0205078,49.5966797z M111.5825195,48.8608398 c-2.9707031,0-3.6494141,1.6313477-3.6494141,3.6508789v1.1347656h7.2944336v-1.1347656 C115.2275391,50.4921875,114.5498047,48.8608398,111.5825195,48.8608398z M56.4926758,62.8984375 c0,1.5976562,0.7539062,2.4277344,2.4179688,2.4277344c1.7851562,0,2.8417969-0.5820312,4.1445312-1.1796875v-3.1992188 h-3.9033203C57.3046875,60.9472656,56.4926758,61.2910156,56.4926758,62.8984375z M167.6835938,49.5966797 c-2.9755859,0-4.0068359,1.6240234-4.0068359,3.6455078v7.3789062c0,2.0273438,1.03125,3.65625,4.0068359,3.65625 c2.9677734,0,4.0068359-1.6289062,4.0068359-3.65625v-7.3789062 C171.6904297,51.2207031,170.6513672,49.5966797,167.6835938,49.5966797z M41.9091797,71.171875h-8.7475586V49.9975586h-4.371582 v-7.2963867h4.371582v-4.3808594c0-5.9526367,2.4672852-9.4921875,9.4790039-9.4921875h5.8378906v7.2978516h-3.6489258 c-2.7299805,0-2.909668,1.0195312-2.909668,2.9228516l-0.0107422,3.6523438h6.6103516l-0.7734375,7.2963867h-5.8369141V71.171875 z M71.8061523,71.2265625H64.515625l-0.315918-1.84375c-3.3291016,1.84375-6.300293,2.1425781-8.2602539,2.1425781 c-5.3466797,0-8.1933594-3.5722656-8.1933594-8.5117188c0-5.828125,3.3208008-7.9082031,9.262207-7.9082031h6.046875v-1.2597656 c0-2.9741211-0.3408203-3.8481445-4.9155273-3.8481445H50.659668l0.7314453-7.2963867h8.1762695 c10.0380859,0,12.2387695,3.1708984,12.2387695,11.203125V71.2265625z M96.5991211,50.5327148 c-4.5371094-0.7773438-5.8398438-0.9482422-8.0229492-0.9482422c-3.9213867,0-5.1064453,0.8652344-5.1064453,4.1948242v6.2988281 c0,3.3300781,1.1850586,4.1992188,5.1064453,4.1992188c2.1831055,0,3.4858398-0.1738281,8.0229492-0.9550781v7.1171875 c-3.9741211,0.890625-6.5629883,1.125-8.7514648,1.125c-9.3920898,0-13.1254883-4.9394531-13.1254883-12.0742188v-5.1113281 c0-7.1411133,3.7333984-12.0888672,13.1254883-12.0888672c2.1884766,0,4.7773438,0.2353516,8.7514648,1.1298828V50.5327148z M123.9750977,59.4902344h-16.0419922v0.5878906c0,3.3300781,1.1860352,4.1992188,5.1064453,4.1992188 c3.5244141,0,5.675293-0.1738281,10.2041016-0.9550781v7.1171875c-4.3676758,0.890625-6.6435547,1.125-10.9291992,1.125 c-9.3925781,0-13.1279297-4.9394531-13.1279297-12.0742188v-5.84375c0-6.2426758,2.7714844-11.3564453,12.3959961-11.3564453 s12.3925781,5.0541992,12.3925781,11.3564453V59.4902344z M152.4121094,59.625 c0,6.8964844-1.9707031,11.9257812-13.9111328,11.9257812c-4.3115234,0-6.8408203-0.3789062-11.5996094-1.1113281V31.019043 l8.7451172-1.4594727v13.7890625c1.8896484-0.7021484,4.3359375-1.0585938,6.5625-1.0585938 c8.7460938,0,10.203125,3.9208984,10.203125,10.2216797V59.625z M180.4453125,59.7753906 c0,5.9492188-2.4560547,11.71875-12.7324219,11.71875c-10.28125,0-12.7832031-5.7695312-12.7832031-11.71875V54.03125 c0-5.9521484,2.5019531-11.7226562,12.7832031-11.7226562c10.2763672,0,12.7324219,5.7705078,12.7324219,11.7226562V59.7753906z M208.4589844,59.7753906c0,5.9492188-2.4589844,11.71875-12.7333984,11.71875 c-10.28125,0-12.7832031-5.7695312-12.7832031-11.71875V54.03125c0-5.9521484,2.5019531-11.7226562,12.7832031-11.7226562 c10.2744141,0,12.7333984,5.7705078,12.7333984,11.7226562V59.7753906z M237.2080078,71.171875h-9.4794922l-8.0166016-13.3828125 V71.171875h-8.7480469V31.019043l8.7480469-1.4594727v25.8486328l8.0166016-12.7070312h9.4794922l-8.7519531,13.8671875 L237.2080078,71.171875z M195.6962891,49.5966797c-2.9707031,0-4.0019531,1.6240234-4.0019531,3.6455078v7.3789062 c0,2.0273438,1.03125,3.65625,4.0019531,3.65625c2.9667969,0,4.0166016-1.6289062,4.0166016-3.65625v-7.3789062 C199.7128906,51.2207031,198.6630859,49.5966797,195.6962891,49.5966797z"/>
<path fill="#FFFFFF" d="M242.2011719,66.1777344c1.4726562,0,2.6464844,1.2011719,2.6464844,2.7011719 c0,1.5234375-1.1738281,2.7109375-2.6572266,2.7109375c-1.4755859,0-2.6728516-1.1875-2.6728516-2.7109375 c0-1.5,1.1972656-2.7011719,2.6728516-2.7011719H242.2011719z M242.1904297,66.5976562 c-1.1865234,0-2.1582031,1.0214844-2.1582031,2.28125c0,1.2832031,0.9716797,2.2910156,2.1689453,2.2910156 c1.1982422,0.0117188,2.1552734-1.0078125,2.1552734-2.2792969s-0.9570312-2.2929688-2.1552734-2.2929688H242.1904297z M241.6865234,70.4511719h-0.4804688V67.4375c0.2519531-0.0351562,0.4921875-0.0703125,0.8515625-0.0703125 c0.4560547,0,0.7539062,0.0957031,0.9365234,0.2265625c0.1767578,0.1328125,0.2724609,0.3359375,0.2724609,0.6230469 c0,0.3984375-0.2617188,0.6367188-0.5849609,0.734375v0.0234375c0.2626953,0.0488281,0.4423828,0.2871094,0.5029297,0.7304688 c0.0703125,0.46875,0.1425781,0.6484375,0.1904297,0.7460938h-0.5029297 c-0.0712891-0.0976562-0.1435547-0.3730469-0.2041016-0.7695312c-0.0703125-0.3828125-0.2636719-0.5273438-0.6484375-0.5273438 h-0.3330078V70.4511719z M241.6865234,68.7832031h0.3476562c0.3935547,0,0.7285156-0.1445312,0.7285156-0.5175781 c0-0.2636719-0.1904297-0.5273438-0.7285156-0.5273438c-0.1572266,0-0.265625,0.0117188-0.3476562,0.0234375V68.7832031z"/>
</g>
</svg>
<!-- FACEBOOK LINK-->
Leave A Review
</div>
<div class="review-right">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 272 92" width="272" height="92">
<path fill="#EA4335" d="M115.75 47.18c0 12.77-9.99 22.18-22.25 22.18s-22.25-9.41-22.25-22.18C71.25 34.32 81.24 25 93.5 25s22.25 9.32 22.25 22.18zm-9.74 0c0-7.98-5.79-13.44-12.51-13.44S80.99 39.2 80.99 47.18c0 7.9 5.79 13.44 12.51 13.44s12.51-5.55 12.51-13.44z"/>
<path fill="#FBBC05" d="M163.75 47.18c0 12.77-9.99 22.18-22.25 22.18s-22.25-9.41-22.25-22.18c0-12.85 9.99-22.18 22.25-22.18s22.25 9.32 22.25 22.18zm-9.74 0c0-7.98-5.79-13.44-12.51-13.44s-12.51 5.46-12.51 13.44c0 7.9 5.79 13.44 12.51 13.44s12.51-5.55 12.51-13.44z"/>
<path fill="#4285F4" d="M209.75 26.34v39.82c0 16.38-9.66 23.07-21.08 23.07-10.75 0-17.22-7.19-19.66-13.07l8.48-3.53c1.51 3.61 5.21 7.87 11.17 7.87 7.31 0 11.84-4.51 11.84-13v-3.19h-.34c-2.18 2.69-6.38 5.04-11.68 5.04-11.09 0-21.25-9.66-21.25-22.09 0-12.52 10.16-22.26 21.25-22.26 5.29 0 9.49 2.35 11.68 4.96h.34v-3.61h9.25zm-8.56 20.92c0-7.81-5.21-13.52-11.84-13.52-6.72 0-12.35 5.71-12.35 13.52 0 7.73 5.63 13.36 12.35 13.36 6.63 0 11.84-5.63 11.84-13.36z"/>
<path fill="#34A853" d="M225 3v65h-9.5V3h9.5z"/>
<path fill="#EA4335" d="M262.02 54.48l7.56 5.04c-2.44 3.61-8.32 9.83-18.48 9.83-12.6 0-22.01-9.74-22.01-22.18 0-13.19 9.49-22.18 20.92-22.18 11.51 0 17.14 9.16 18.98 14.11l1.01 2.52-29.65 12.28c2.27 4.45 5.8 6.72 10.75 6.72 4.96 0 8.4-2.44 10.92-6.14zm-23.27-7.98l19.82-8.23c-1.09-2.77-4.37-4.7-8.23-4.7-4.95 0-11.84 4.37-11.59 12.93z"/>
<path fill="#4285F4" d="M35.29 41.41V32H67c.31 1.64.47 3.58.47 5.68 0 7.06-1.93 15.79-8.15 22.01-6.05 6.3-13.78 9.66-24.02 9.66C16.32 69.35.36 53.89.36 34.91.36 15.93 16.32.47 35.3.47c10.5 0 17.98 4.12 23.6 9.49l-6.64 6.64c-4.03-3.78-9.49-6.72-16.97-6.72-13.86 0-24.7 11.17-24.7 25.03 0 13.86 10.84 25.03 24.7 25.03 8.99 0 14.11-3.61 17.39-6.89 2.66-2.66 4.41-6.46 5.1-11.65l-22.49.01z"/>
</svg>
<!-- GOOGLE LINK -->
Leave A Review
</div>
</div>
</div>
<div class="hide" id="terrible"><h3>We're sorry that you had such a terrible experience with our product. As hard as it is to swallow a bitter pill, we appreciate your feedback and will look into how we can improve the areas you shared with us. We're always improving and hope you'll give our product another try in the future.</h3>
<form action="/action_page.php">
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname" value="Doe"><br><br>
<input type="submit" value="Submit">
</form>
</div>
How can I fix this behavior? Thanks

You can take the Submit Answer Button Handler out of the "#question .choice" click handler.
var ranking1 = 0;
$('#question-1 .choice').on('click', function () {
ranking1 = $(this).attr('data-value');
});
$('.answer-submit').on('click', function () {
console.log(ranking1)
sessionStorage.setItem('ranking1', ranking1);
$('#question-1').addClass('hide');
$('.summary').removeClass('hide');
var total;
function calculateTotal() {
var ranking1 = sessionStorage.getItem('ranking1');
var total = parseInt(ranking1) + 0;
if (total >= 4) {
$('.amazing').removeClass('hide');
} else if ((total < 4) && (total = 3)) {
$('#terrible').removeClass('hide');
} else {
$('#terrible').removeClass('hide');
}
}
calculateTotal();
});
Let me know if you still face any issue.

Related

Make a table scrollable with html/css

I made a DashBoard and in my mailPage I can't make it scrollable.
I upload my website if you have time to check my problem.
My Code
window.addEventListener("load", function () {
var deleteSVG = '<svg class="deleteSVG" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M20.2871 5.24297C20.6761 5.24297 21 5.56596 21 5.97696V6.35696C21 6.75795 20.6761 7.09095 20.2871 7.09095H3.71385C3.32386 7.09095 3 6.75795 3 6.35696V5.97696C3 5.56596 3.32386 5.24297 3.71385 5.24297H6.62957C7.22185 5.24297 7.7373 4.82197 7.87054 4.22798L8.02323 3.54598C8.26054 2.61699 9.0415 2 9.93527 2H14.0647C14.9488 2 15.7385 2.61699 15.967 3.49699L16.1304 4.22698C16.2627 4.82197 16.7781 5.24297 17.3714 5.24297H20.2871ZM18.8058 19.134C19.1102 16.2971 19.6432 9.55712 19.6432 9.48913C19.6626 9.28313 19.5955 9.08813 19.4623 8.93113C19.3193 8.78413 19.1384 8.69713 18.9391 8.69713H5.06852C4.86818 8.69713 4.67756 8.78413 4.54529 8.93113C4.41108 9.08813 4.34494 9.28313 4.35467 9.48913C4.35646 9.50162 4.37558 9.73903 4.40755 10.1359C4.54958 11.8992 4.94517 16.8102 5.20079 19.134C5.38168 20.846 6.50498 21.922 8.13206 21.961C9.38763 21.99 10.6811 22 12.0038 22C13.2496 22 14.5149 21.99 15.8094 21.961C17.4929 21.932 18.6152 20.875 18.8058 19.134Z" fill="#200E32"/></svg>'
nbMail = 100;
for (i = 0; i < nbMail; i++) {
document.getElementById("mailTableBody").innerHTML += '<tr class="mail mail' + i + '"><td class="mailSender">toto#toto.com</td><td class="mailObject">toto object</td><td class="mailFirstCaracters">Hello World !</td><td class="mailDate">Monday 30 december 2033 11:22PM</td><td>' + deleteSVG + '</td></tr>';
}
});
html{
overflow: hidden;
}
.mailTableBody{
display: inline-block;
padding: 2% 2% 2% 2%;
overflow-y: auto;
width: 50%;
height: 67%;
}
.mail {
width: 50%;
height: 80%;
background: green;
border: 1px solid black;
border-radius: 10px;
box-shadow: 0 0 1px 1px black;
margin-top: 2%;
position: relative;
margin-bottom: 30%;
}
.deleteSVG{
height: 20%;
}
<div class="page mailPage" id="mailPage">
<table class="mailTable">
<tbody class="mailTableBody" id="mailTableBody">
</tbody>
</table>
</div>
Thanks a lot.
https://edtmimi.000webhostapp.com/dashBoard/
Try overriding the overflow : hidden property of the html class, in your .mailPage page like so :
.mailPage .page {
/* added below line */
overflow: scroll !important;
}

Make absolute-positioned children dynamically resize with their ascendants

(Please ignore the empty squares.)
without CSS view { height: 45em; }, I get: (position overlap)
with CSS view { height: 45em; }, I get: (unwanted, position mismatch)
How can I have the blue <span> element positioned correctly in the second case?
<view style="height: 45em;">
<pdf-page> <!-- position: relative -->
<text class="textLayer"> <!-- position: absolute -->
<span style="left: 417.34px; top: 37.8391px; ..."></span> <!-- position: absolute -->
</text>
<svg width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842" xmlns="http://www.w3.org/2000/svg" version="1.1">
<g ⋯><g ⋯><text><tspan></tspan></text></g></g>
</svg>
</pdf-page>
</view>
Here is the complete case in stackoverflow (see /* ← */ in the second pane after clicking on Show code snippet):
#namespace url(http://www.w3.org/1999/xhtml);
#namespace svg url(http://www.w3.org/2000/svg);
/*pdf.css*/
:root {
--pdf-page-outline-color: #aaa;
--pdf-page-background-color: #fcfcfc;
}
pdf-file { display: contents; }
pdf-page {
display: inline-block;
outline: 1px solid var(--pdf-page-outline-color);
background-color: var(--pdf-page-background-color);
}
pdf-page { position: relative; }
/* text.css */
.textLayer {
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
width: 100%; height: 100%;
-overflow: hidden;
opacity: 1;
-line-height: 1;
}
.textLayer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
/**/
view { background: green; }
.textLayer { background: rgba(0, 255, 0, .1); }
svg|svg { background: rgba(255, 0, 0, .1); }
<style>
view {
height: 45em; /* ← */
display: flex;
overflow: auto;
flex-direction: column;
place-items: center;
scroll-snap-type: y mandatory;
overflow: auto;
}
pdf-page { height: 100%; scroll-snap-align: start; }
svg { height: 100%; width: auto; }
text { overflow: visible; background: rgb(0, 0, 0, .1); }
text > span { background: rgba(0,0,255,.1); }
</style>
<view -onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" svg="">
<text class="textLayer">
<span style="left: 417.34px; top: 37.8391px; font-size: 12px; font-family: sans-serif; transform: scaleX(1.07482);">Plenarprotokoll 16/3</span>
</text>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842">
<g transform="matrix(1 0 0 -1 -8 850)">
<g transform="">
<text transform="matrix(12 0 0 12 425.34 801.2976) scale(1, -1)" xml:space="preserve">
<tspan x="0 0.6672 0.9454 1.5016 2.1128 2.669 3.0582 3.6694 4.0586 4.6698 5.003 5.6142 6.1704 6.7816 7.0598 7.6132 8.1694 8.7256 9.0038" y="0" font-family="g_d0_f1" font-size="1px" fill="rgb(0,0,0)"></tspan>
</text>
</g>
</g>
</svg>
</pdf-page>
</view>
(also available for review on codepen: https://codepen.io/cetinsert/pen/MWeVxLe?editors=1100)
A much more precise way is to just transform: scale(x, y) the <text> layer once on resize without any <span style> position value recalculations / unit change.
This answer has triggered the launch of my commercial project.
– https://WebPDF.pro
Zero-dependency, truly HTML-native PDF web components.
const t = document.querySelector('text');
const r = new ResizeObserver(textResize(t));
r.observe(t);
const textResize = t => ([ a ]) => {
const e = t.parentNode.lastElementChild; // <svg> | <canvas>
const i = PDFPageElement.image(e); // { height, width };
const h = e.clientHeight;
const x = h / i. height;
const w = e.clientWidth;
const y = w / i. width;
t.style.setProperty('transform', `scale(${x}, ${y})`);
};
PDFPageElement.image = i => { if (!i) return;
switch (i.tagName) {
case 'CANVAS': return { height: i.height, width: i.width };
default: /*SVG*/ return { height: i.height.baseVal.value, width: i.width.baseVal.value };
}
};
with 1 additional CSS rule
.textLayer { overflow: visible; }
Before / After
Given viewport width and height, a one-time conversion from <span style> pixels to percents:
const px2pc = ({ width, height }) => s => {
const c = s.style;
const l = +c.getPropertyValue('left' ).slice(0, -2); // drop px
const t = +c.getPropertyValue('top' ).slice(0, -2);
const f = +c.getPropertyValue('font-size').slice(0, -2);
c.setProperty ('left', `${(l / width) * 100}%`);
c.setProperty ('top', `${(t / height) * 100}%`);
c.setProperty ('font-size', `${(f / height) * 100}%`);
};
and accounting for font size adaptation in <text> element whenever its ancestors cause resize:
const t = document.querySelector('text');
const r = new ResizeObserver(textFontResize(t));
r.observe(t);
const textFontResize = t => ([ a ]) => {
const i = t.parentNode.lastElementChild; // <svg> | <canvas>
t.style.setProperty('font-size', `${i.clientHeight}px`);
};
proved itself a very robust and relatively simple solution.
(If anyone comes up with a more elegant way, say without ever resorting to ResizeObserver, please post a new answer.)
Demo
(External assets are version-fixed for this question.)
Scroll to the end of this answer
Hit ▶️ Run code snippet
Hit ⤤ Full page
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.min.js" integrity="sha512-Z8CqofpIcnJN80feS2uccz+pXWgZzeKxDsDNMD/dJ6997/LSRY+W4NmEt9acwR+Gt9OHN0kkI1CTianCwoqcjQ==" crossorigin="anonymous"></script>
<script src="//shin.glitch.me/shin.q1.js"></script>
<script src="//shin.glitch.me/pdf.q1.js"></script>
<!-- private resources -->
<link href="//cdn.blue/{fa-5.15}/css/all.css" rel="stylesheet">
<link href="//cdn.blue/{fa+}/var.css" rel="stylesheet">
<link href="//cdn.blue/{fa+}/x.css" rel="stylesheet">
<!-- private resources -->
<style>:root { max-width: 50em; margin: auto; }</style>
<script>console.clear();</script>
<style>html, body { padding: 0; margin: 0; font-family: system-ui; }</style>
<script>
class CodeEditElement extends ShinElement {
constructor() {
super(`
<style>:host { display: block; overflow: hidden; } pre { height: 100%; margin: 0; }</style>
<pre contenteditable spellcheck=false inputmode=text></pre>`, { _: { QS: { T: [ 'pre' ] } } });
const e = this;
e.ph = v => { const e = v.target; if (!e.childElementCount) return; e.textContent = e.textContent; };
}
connectedCallback() { this._.pre. addEventListener('input', this.ph); }
disconnectedCallback() { this._.pre.removeEventListener('input', this.ph); }
get textContent() { return this._.pre.textContent; }
set textContent(v) { this._.pre.textContent = v; }
}
CodeEditElement.define();
class CodeLiveElement extends ShinElement {
constructor() { super(`<live></live>`, { _: { QS: { T: [ 'live' ] } } }); }
get textContent() { return this._.live.textContent; }
set textContent(v) { this._.live.textContent = v; }
get innerHTML() { return this._.live.innerHTML; }
set innerHTML(v) { this._.live.innerHTML = v; this.evalScripts(); }
evalScripts() { this._.QA('script').forEach(s => eval(s.textContent)); }
}
CodeLiveElement.define();
class CodePlayElement extends ShinElement {
constructor() {
super(`
<style>
:host(:not([open])) > code-edit { display: none; }
:host > div { display: flex; justify-content: stretch; align-items: stretch; }
::slotted(select) { flex: 1; }
* { border-color: var(--bd); }
</style>
<div part=controls>
<slot></slot>
<button id=reset><slot name=reset></slot></button>
<button id=open><slot name=open></slot></button>
</div>
<code-edit id=pre part=edit></code-edit>`,
{ _: { QS: { S: { '#pre': 'pre', '#reset': 'reset', '#open': 'open' } } } }
);
const e = this;
e.sc = v => { const tx = e.tx; e.px = tx; };
e.pi = v => { e.t.ux = e.px; };
e.rc = v => { e.tr(); };
e.oc = v => { e.open =!e.open; };
Shin.IPA(e, 'open', { t: Shin.Boolean });
}
connectedCallback() { setTimeout(() => this._init()); }
disconnectedCallback() { this._dest(); }
static cleanCode(t = "") {
return t.trim().replace(/^[\n]+/g, '').replace(/\s+$/g, '').split('\n').map(l => l.trimEnd()).join('\n');
}
get s() { return this.QS('select'); }
get S() { const o = this.QA('option'); return o.filter(o => o.selected); }
get t() { return [].concat(...this.S.map(o => Shin.QA('template', o))); }
get tx() { return this.t.map(t => t.ux || this.constructor.cleanCode(t.innerHTML)).join('\n'); }
tr() { this.t.ux = undefined; this.sc(); }
get r() { return this._.reset; }
get o() { return this._.open; }
get p() { return this._.pre; }
get P() { return this._.QA('pre'); }
get px() { return this.p.textContent; }
set px(v) { this.p.textContent = v; this.oninput(); }
_init() {
const e = this; e.sc();
e.s.addEventListener('change', e.sc);
e.p.addEventListener('input', e.pi);
e.r.addEventListener('click', e.rc);
e.o.addEventListener('click', e.oc);
}
_dest() {
const e = this;
e.s.removeEventListener('change', e.sc);
e.p.removeEventListener('input', e.pi);
e.p.removeEventListener('click', e.rc);
e.p.removeEventListener('click', e.oc);
}
}
CodePlayElement.define();
</script>
<style>
body { padding: 1em; overflow: scroll; font-family: system-ui; }
:root {
--list-bg: #eee;
--code-bg: #fefefe;
--live-bg_: #ccc;
--bd: #ccc;
}
code-play { display: flex; width: 100%; flex-direction: row-reverse; }
code-play:not(:first-of-type) { margin-top: 1em; }
::part(edit) { min-height: 1em; min-width: 1em; overflow-x: auto; background-color: var(--code-bg); }
x[undo]:before, x[undo]:after { content: var(--fa-undo); }
x[open]:before, x[open]:after { content: var(--fa-eye-slash); }
[open] x[open]:before,
[open] x[open]:after { content: var(--fa-eye); }
select { background: var(--list-bg); border-color: var(--bd); overflow: auto; }
live { background: var(--live-bg); display: block; bordxer: 1px solid var(--bd); }
code-play:not([open]) + live { _display: none; }
::part(edit) { border: 1px solid var(--bd); flex: 1; }
::part(controls) { flex-direction: column-reverse; }
::part() { border-radius: 3px; }
</style>
<style>
code-play:not([open]) { height: 2.7em; _outline: 1px solid; }
code-play:not([open]) > select { display: none; }
</style>
</head>
<body>
<code-live id="cl"></code-live><script>cl.innerHTML = "";</script>
<script>
const pes = 'previousElementSibling';
const n = (p, N = 1) => e => { let j = e[p]; for (let i = 1; i < N; i++) j = j[p]; return j; };
const c = n(pes, 2);
const l = n(pes, 1);
const _ = () => document.currentScript;
</script>
<code-play open>
<select multiple size="1">
<option selected>file<template>
<pdf-file id="f" src="//pdf.systems/16003.pdf"></pdf-file>
<pdf-file id="g" src="//pdf.systems/16004.pdf"></pdf-file>
</template></option>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{
const _ = document.currentScript;
c(_).oninput = () =>
l(_).innerHTML = c(_).px;
}
</script>
<code-play open style="min-height: 11em;">
<select multiple size="6">
<optgroup label="File Reference">
<option>by attribute<!-- !!!!!!!!! --><template>
<pdf-page of="f" no="+1" scale=".1"></pdf-page>
<pdf-page of="f" no="+1" scale=".2"></pdf-page>
<pdf-page of="f" no="+1" scale=".3"></pdf-page>
<pdf-page of="f" no="+1" scale=".4"></pdf-page>
<pdf-page of="f" no="+1" scale=".5"></pdf-page>
<pdf-page of="f" no="+1" scale=".5" svg=""></pdf-page>
</template></option>
<option>by ancestry<!-- !!!!!!!!! --><template>
<pdf-file src="//pdf.systems/16008.pdf">
<pdf-page no="+1" scale=".4" svg></pdf-page>
<pdf-page no="+3" scale=".4" svg></pdf-page>
<pdf-page no="-1" scale=".4" svg></pdf-page>
</pdf-file>
</template></option>
</optgroup>
<optgroup label="Embed Mode">
<option selected>Sized Container ⭤<!-- !!!!!!!!! --><template>
<style>
view { width: 10em; height: 25em; /* ← */
display: block; background: white; overflow: auto;
}
pdf-page { width: 100%; }
::part(layer) { width: 100%; height: auto; }
</style>
<view onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" xvg="" scale=".2"></pdf-page>
<pdf-page of="f" no="+1" xvg="" scale="1"></pdf-page>
<pdf-page of="f" no="+1" xvg="" scale="2"></pdf-page>
<pdf-page of="f" no="+1" svg=""></pdf-page>
<pdf-page of="f" no="-1" xvg=""></pdf-page>
<pdf-page of="g" no="-1" svg=""></pdf-page>
</view>
</template></option>
</optgroup>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{ const _ = document.currentScript; c(_).oninput = () => l(_).innerHTML = c(_).px; }</script>
<style>live { display: flex; align-items: flex-end; flex-wrap: wrap; } pdf-file { display: contents; }</style>
<h3>Styling</h3>
<p>Styles can be easily applied. (Try <strong><kbd>Ctrl</kbd></strong> + <i class="fa fa-mouse-pointer"></i> to unselect / select multiple.)</p>
<code-play open>
<select multiple size="8">
<optgroup label="Page">
<option>outline <template><style>pdf-page { outline: 1px dotted; }</style></template></option>
<option>background<template><style>pdf-page { background-color: rgb(200, 200, 255, .1); }</style></template></option>
</optgroup>
<optgroup label="Text">
<option selected>mark<template><style>::part(span) { background-color: rgb(255, 0, 0, .1); }</style></template></option>
</optgroup>
<optgroup label="Image">
<option>hidden <template><style>::part(image) { opacity: 0; }</style></template></option>
<option>pixelated <template><style>::part(image) { image-rendering: pixelated; }</style></template></option>
<option>crisp-edges<template><style>::part(image) { image-rendering: crisp-edges; }</style></template></option>
</optgroup>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{ const _ = document.currentScript; c(_).oninput = () => l(_).innerHTML = c(_).px; }</script>
<script>
document.addEventListener( 'load', e => console.warn('l', e.target));
document.addEventListener('unload', e => console.warn('u', e.target));
</script>
<p style="margin-bottom: 10em;">Documentation (WIP)</p>
</body>
</html>
It's not really possible to do via CSS in a clean way. This, for example, will work, but because you're positioning a span on top of a picture, all of the numbers are hardcoded:
.textLayer > span{
right: 10% !important;
left: auto !important;
top: 0 !important;
margin-top: 6%;/*margin-top uses 6% of the WIDTH, not 6% of the height. It's very useful when trying to place something on top of an image.*/
width: 20%;
height: 2%;
}
Here's a reproduction of your snippet with the CSS added in:
#namespace url(http://www.w3.org/1999/xhtml);
#namespace svg url(http://www.w3.org/2000/svg);
/*pdf.css*/
:root {
--pdf-page-outline-color: #aaa;
--pdf-page-background-color: #fcfcfc;
}
pdf-file { display: contents; }
pdf-page {
display: inline-block;
outline: 1px solid var(--pdf-page-outline-color);
background-color: var(--pdf-page-background-color);
}
pdf-page { position: relative; }
/* text.css */
.textLayer {
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
width: 100%; height: 100%;
-overflow: hidden;
opacity: 1;
line-height: 1;
}
.textLayer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
.textLayer > span{
right: 10% !important;
left: auto !important;
top: 0 !important;
margin-top: 6%;/*margin-top uses 6% of the WIDTH, not the height. It's sometimes more useful than ordinary top:6%.*/
width: 20%;
height: 2%;
}
/**/
view { background: green; }
.textLayer { background: rgba(0, 255, 0, .1); }
svg|svg { background: rgba(255, 0, 0, .1); }
<style>
view {
height: 45em; /* ← */
display: flex;
overflow: auto;
flex-direction: column;
place-items: center;
scroll-snap-type: y mandatory;
overflow: auto;
}
pdf-page { height: 100%; scroll-snap-align: start; }
svg { height: 100%; width: auto; }
text { overflow: visible; background: rgb(0, 0, 0, .1); }
text > span { background: rgba(0,0,255,.1); }
</style>
<view -onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" svg="">
<text class="textLayer">
<span style="left: 417.34px; top: 37.8391px; font-size: 12px; font-family: sans-serif; transform: scaleX(1.07482);">Plenarprotokoll 16/3</span>
</text>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842">
<g transform="matrix(1 0 0 -1 -8 850)">
<g transform="">
<text transform="matrix(12 0 0 12 425.34 801.2976) scale(1, -1)" xml:space="preserve">
<tspan x="0 0.6672 0.9454 1.5016 2.1128 2.669 3.0582 3.6694 4.0586 4.6698 5.003 5.6142 6.1704 6.7816 7.0598 7.6132 8.1694 8.7256 9.0038" y="0" font-family="g_d0_f1" font-size="1px" fill="rgb(0,0,0)"></tspan>
</text>
</g>
</g>
</svg>
</pdf-page>
</view>
Edit: A bit more clarification.
We want to put the box on top of the text. The numbers used for the position and width/height of the text may seem arbitrary, but that's simply because the location of the item we're trying to cover ALSO has an arbitrary location/width/height. (If you like, we can talk about how to use GIMP to check the aspect ratio of your image, but..
a. I don't think using GIMP to measure the correct values is within the scope of this answer (You can math it out by taking the width of the image and height of the image to find the aspect ratio, and then use that aspect ratio along with the X/Y coordinates of the start point and the X/Y coordinates of the end point to figure out what percentages you need to use.... but, well....)
b. It's usually significantly faster to just fiddle with it in Chrome's Dev Tools for 15 minutes,
As a general rule, when using position: absolute to put something on top of an image, your code is going to look something like this:
.item{
position:absolute;
top:0;
margin-top:W%; //The reason we use margin instead of top is because margin is based off width, which allows us to maintain aspect ratio on our positioning.
left:X%; // Or right
width:Y%;
height:Z%;
}
Edit 2: I'd originally used vw and vh, which are often extremely useful for this kind of positioning, but in the end it was possible to refactor them out, which is why the only non-standard positioning we're using is margin-top.

How to Create Multiple Cursors in a single Range Slider? [duplicate]

Is it possible to make a HTML5 slider with two input values, for example to select a price range? If so, how can it be done?
I've been looking for a lightweight, dependency free dual slider for some time (it seemed crazy to import jQuery just for this) and there don't seem to be many out there. I ended up modifying #Wildhoney's code a bit and really like it.
function getVals(){
// Get slider values
var parent = this.parentNode;
var slides = parent.getElementsByTagName("input");
var slide1 = parseFloat( slides[0].value );
var slide2 = parseFloat( slides[1].value );
// Neither slider will clip the other, so make sure we determine which is larger
if( slide1 > slide2 ){ var tmp = slide2; slide2 = slide1; slide1 = tmp; }
var displayElement = parent.getElementsByClassName("rangeValues")[0];
displayElement.innerHTML = slide1 + " - " + slide2;
}
window.onload = function(){
// Initialize Sliders
var sliderSections = document.getElementsByClassName("range-slider");
for( var x = 0; x < sliderSections.length; x++ ){
var sliders = sliderSections[x].getElementsByTagName("input");
for( var y = 0; y < sliders.length; y++ ){
if( sliders[y].type ==="range" ){
sliders[y].oninput = getVals;
// Manually trigger event first time to display values
sliders[y].oninput();
}
}
}
}
section.range-slider {
position: relative;
width: 200px;
height: 35px;
text-align: center;
}
section.range-slider input {
pointer-events: none;
position: absolute;
overflow: hidden;
left: 0;
top: 15px;
width: 200px;
outline: none;
height: 18px;
margin: 0;
padding: 0;
}
section.range-slider input::-webkit-slider-thumb {
pointer-events: all;
position: relative;
z-index: 1;
outline: 0;
}
section.range-slider input::-moz-range-thumb {
pointer-events: all;
position: relative;
z-index: 10;
-moz-appearance: none;
width: 9px;
}
section.range-slider input::-moz-range-track {
position: relative;
z-index: -1;
background-color: rgba(0, 0, 0, 1);
border: 0;
}
section.range-slider input:last-of-type::-moz-range-track {
-moz-appearance: none;
background: none transparent;
border: 0;
}
section.range-slider input[type=range]::-moz-focus-outer {
border: 0;
}
<!-- This block can be reused as many times as needed -->
<section class="range-slider">
<span class="rangeValues"></span>
<input value="5" min="0" max="15" step="0.5" type="range">
<input value="10" min="0" max="15" step="0.5" type="range">
</section>
No, the HTML5 range input only accepts one input. I would recommend you to use something like the jQuery UI range slider for that task.
Coming late, but noUiSlider avoids having a jQuery-ui dependency, which the accepted answer does not. Its only "caveat" is IE support is for IE9 and newer, if legacy IE is a deal breaker for you.
It's also free, open source and can be used in commercial projects without restrictions.
Installation: Download noUiSlider, extract the CSS and JS file somewhere in your site file system, and then link to the CSS from head and to JS from body:
<!-- In <head> -->
<link href="nouislider.min.css" rel="stylesheet">
<!-- In <body> -->
<script src="nouislider.min.js"></script>
Example usage: Creates a slider which goes from 0 to 100, and starts set to 20-80.
HTML:
<div id="slider">
</div>
JS:
var slider = document.getElementById('slider');
noUiSlider.create(slider, {
start: [20, 80],
connect: true,
range: {
'min': 0,
'max': 100
}
});
Sure you can simply use two sliders overlaying each other and add a bit of javascript (actually not more than 5 lines) that the selectors are not exceeding the min/max values (like in #Garys) solution.
Attached you'll find a short snippet adapted from a current project including some CSS3 styling to show what you can do (webkit only). I also added some labels to display the selected values.
It uses JQuery but a vanillajs version is no magic though.
#Update: The code below was just a proof of concept. Due to many requests I've added a possible solution for Mozilla Firefox (without changing the original code). You may want to refractor the code below before using it.
(function() {
function addSeparator(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + '.' + '$2');
}
return x1 + x2;
}
function rangeInputChangeEventHandler(e){
var rangeGroup = $(this).attr('name'),
minBtn = $(this).parent().children('.min'),
maxBtn = $(this).parent().children('.max'),
range_min = $(this).parent().children('.range_min'),
range_max = $(this).parent().children('.range_max'),
minVal = parseInt($(minBtn).val()),
maxVal = parseInt($(maxBtn).val()),
origin = $(this).context.className;
if(origin === 'min' && minVal > maxVal-5){
$(minBtn).val(maxVal-5);
}
var minVal = parseInt($(minBtn).val());
$(range_min).html(addSeparator(minVal*1000) + ' €');
if(origin === 'max' && maxVal-5 < minVal){
$(maxBtn).val(5+ minVal);
}
var maxVal = parseInt($(maxBtn).val());
$(range_max).html(addSeparator(maxVal*1000) + ' €');
}
$('input[type="range"]').on( 'input', rangeInputChangeEventHandler);
})();
body{
font-family: sans-serif;
font-size:14px;
}
input[type='range'] {
width: 210px;
height: 30px;
overflow: hidden;
cursor: pointer;
outline: none;
}
input[type='range'],
input[type='range']::-webkit-slider-runnable-track,
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
background: none;
}
input[type='range']::-webkit-slider-runnable-track {
width: 200px;
height: 1px;
background: #003D7C;
}
input[type='range']:nth-child(2)::-webkit-slider-runnable-track{
background: none;
}
input[type='range']::-webkit-slider-thumb {
position: relative;
height: 15px;
width: 15px;
margin-top: -7px;
background: #fff;
border: 1px solid #003D7C;
border-radius: 25px;
z-index: 1;
}
input[type='range']:nth-child(1)::-webkit-slider-thumb{
z-index: 2;
}
.rangeslider{
position: relative;
height: 60px;
width: 210px;
display: inline-block;
margin-top: -5px;
margin-left: 20px;
}
.rangeslider input{
position: absolute;
}
.rangeslider{
position: absolute;
}
.rangeslider span{
position: absolute;
margin-top: 30px;
left: 0;
}
.rangeslider .right{
position: relative;
float: right;
margin-right: -5px;
}
/* Proof of concept for Firefox */
#-moz-document url-prefix() {
.rangeslider::before{
content:'';
width:100%;
height:2px;
background: #003D7C;
display:block;
position: relative;
top:16px;
}
input[type='range']:nth-child(1){
position:absolute;
top:35px !important;
overflow:visible !important;
height:0;
}
input[type='range']:nth-child(2){
position:absolute;
top:35px !important;
overflow:visible !important;
height:0;
}
input[type='range']::-moz-range-thumb {
position: relative;
height: 15px;
width: 15px;
margin-top: -7px;
background: #fff;
border: 1px solid #003D7C;
border-radius: 25px;
z-index: 1;
}
input[type='range']:nth-child(1)::-moz-range-thumb {
transform: translateY(-20px);
}
input[type='range']:nth-child(2)::-moz-range-thumb {
transform: translateY(-20px);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div class="rangeslider">
<input class="min" name="range_1" type="range" min="1" max="100" value="10" />
<input class="max" name="range_1" type="range" min="1" max="100" value="90" />
<span class="range_min light left">10.000 €</span>
<span class="range_max light right">90.000 €</span>
</div>
Actually I used my script in html directly. But in javascript when you add oninput event listener for this event it gives the data automatically.You just need to assign the value as per your requirement.
[slider] {
width: 300px;
position: relative;
height: 5px;
margin: 45px 0 10px 0;
}
[slider] > div {
position: absolute;
left: 13px;
right: 15px;
height: 5px;
}
[slider] > div > [inverse-left] {
position: absolute;
left: 0;
height: 5px;
border-radius: 10px;
background-color: #CCC;
margin: 0 7px;
}
[slider] > div > [inverse-right] {
position: absolute;
right: 0;
height: 5px;
border-radius: 10px;
background-color: #CCC;
margin: 0 7px;
}
[slider] > div > [range] {
position: absolute;
left: 0;
height: 5px;
border-radius: 14px;
background-color: #d02128;
}
[slider] > div > [thumb] {
position: absolute;
top: -7px;
z-index: 2;
height: 20px;
width: 20px;
text-align: left;
margin-left: -11px;
cursor: pointer;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4);
background-color: #FFF;
border-radius: 50%;
outline: none;
}
[slider] > input[type=range] {
position: absolute;
pointer-events: none;
-webkit-appearance: none;
z-index: 3;
height: 14px;
top: -2px;
width: 100%;
opacity: 0;
}
div[slider] > input[type=range]:focus::-webkit-slider-runnable-track {
background: transparent;
border: transparent;
}
div[slider] > input[type=range]:focus {
outline: none;
}
div[slider] > input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
-webkit-appearance: none;
}
div[slider] > input[type=range]::-ms-fill-lower {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-fill-upper {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-tooltip {
display: none;
}
[slider] > div > [sign] {
opacity: 0;
position: absolute;
margin-left: -11px;
top: -39px;
z-index:3;
background-color: #d02128;
color: #fff;
width: 28px;
height: 28px;
border-radius: 28px;
-webkit-border-radius: 28px;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center;
}
[slider] > div > [sign]:after {
position: absolute;
content: '';
left: 0;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top-width: 16px;
border-top-style: solid;
border-top-color: #d02128;
}
[slider] > div > [sign] > span {
font-size: 12px;
font-weight: 700;
line-height: 28px;
}
[slider]:hover > div > [sign] {
opacity: 1;
}
<div slider id="slider-distance">
<div>
<div inverse-left style="width:70%;"></div>
<div inverse-right style="width:70%;"></div>
<div range style="left:0%;right:0%;"></div>
<span thumb style="left:0%;"></span>
<span thumb style="left:100%;"></span>
<div sign style="left:0%;">
<span id="value">0</span>
</div>
<div sign style="left:100%;">
<span id="value">100</span>
</div>
</div>
<input type="range" value="0" max="100" min="0" step="1" oninput="
this.value=Math.min(this.value,this.parentNode.childNodes[5].value-1);
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[1].style.width=value+'%';
children[5].style.left=value+'%';
children[7].style.left=value+'%';children[11].style.left=value+'%';
children[11].childNodes[1].innerHTML=this.value;" />
<input type="range" value="100" max="100" min="0" step="1" oninput="
this.value=Math.max(this.value,this.parentNode.childNodes[3].value-(-1));
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[3].style.width=(100-value)+'%';
children[5].style.right=(100-value)+'%';
children[9].style.left=value+'%';children[13].style.left=value+'%';
children[13].childNodes[1].innerHTML=this.value;" />
</div>
The question was: "Is it possible to make a HTML5 slider with two input values, for example to select a price range? If so, how can it be done?"
In 2020 it is possible to create a fully accessible, native, non-jquery HTML5 slider with two thumbs for price ranges. If found this posted after I already created this solution and I thought that it would be nice to share my implementation here.
This implementation has been tested on mobile Chrome and Firefox (Android) and Chrome and Firefox (Linux). I am not sure about other platforms, but it should be quite good. I would love to get your feedback and improve this solution.
This solution allows multiple instances on one page and it consists of just two inputs (each) with descriptive labels for screen readers. You can set the thumb size in the amount of grid labels. Also, you can use touch, keyboard and mouse to interact with the slider. The value is updated during adjustment, due to the 'on input' event listener.
My first approach was to overlay the sliders and clip them. However, that resulted in complex code with a lot of browser dependencies. Then I recreated the solution with two sliders that were 'inline'. This is the solution you will find below.
var thumbsize = 14;
function draw(slider,splitvalue) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var lower = slider.querySelector('.lower');
var upper = slider.querySelector('.upper');
var legend = slider.querySelector('.legend');
var thumbsize = parseInt(slider.getAttribute('data-thumbsize'));
var rangewidth = parseInt(slider.getAttribute('data-rangewidth'));
var rangemin = parseInt(slider.getAttribute('data-rangemin'));
var rangemax = parseInt(slider.getAttribute('data-rangemax'));
/* set min and max attributes */
min.setAttribute('max',splitvalue);
max.setAttribute('min',splitvalue);
/* set css */
min.style.width = parseInt(thumbsize + ((splitvalue - rangemin)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
max.style.width = parseInt(thumbsize + ((rangemax - splitvalue)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
min.style.left = '0px';
max.style.left = parseInt(min.style.width)+'px';
min.style.top = lower.offsetHeight+'px';
max.style.top = lower.offsetHeight+'px';
legend.style.marginTop = min.offsetHeight+'px';
slider.style.height = (lower.offsetHeight + min.offsetHeight + legend.offsetHeight)+'px';
/* correct for 1 off at the end */
if(max.value>(rangemax - 1)) max.setAttribute('data-value',rangemax);
/* write value and labels */
max.value = max.getAttribute('data-value');
min.value = min.getAttribute('data-value');
lower.innerHTML = min.getAttribute('data-value');
upper.innerHTML = max.getAttribute('data-value');
}
function init(slider) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var rangemin = parseInt(min.getAttribute('min'));
var rangemax = parseInt(max.getAttribute('max'));
var avgvalue = (rangemin + rangemax)/2;
var legendnum = slider.getAttribute('data-legendnum');
/* set data-values */
min.setAttribute('data-value',rangemin);
max.setAttribute('data-value',rangemax);
/* set data vars */
slider.setAttribute('data-rangemin',rangemin);
slider.setAttribute('data-rangemax',rangemax);
slider.setAttribute('data-thumbsize',thumbsize);
slider.setAttribute('data-rangewidth',slider.offsetWidth);
/* write labels */
var lower = document.createElement('span');
var upper = document.createElement('span');
lower.classList.add('lower','value');
upper.classList.add('upper','value');
lower.appendChild(document.createTextNode(rangemin));
upper.appendChild(document.createTextNode(rangemax));
slider.insertBefore(lower,min.previousElementSibling);
slider.insertBefore(upper,min.previousElementSibling);
/* write legend */
var legend = document.createElement('div');
legend.classList.add('legend');
var legendvalues = [];
for (var i = 0; i < legendnum; i++) {
legendvalues[i] = document.createElement('div');
var val = Math.round(rangemin+(i/(legendnum-1))*(rangemax - rangemin));
legendvalues[i].appendChild(document.createTextNode(val));
legend.appendChild(legendvalues[i]);
}
slider.appendChild(legend);
/* draw */
draw(slider,avgvalue);
/* events */
min.addEventListener("input", function() {update(min);});
max.addEventListener("input", function() {update(max);});
}
function update(el){
/* set function vars */
var slider = el.parentElement;
var min = slider.querySelector('#min');
var max = slider.querySelector('#max');
var minvalue = Math.floor(min.value);
var maxvalue = Math.floor(max.value);
/* set inactive values before draw */
min.setAttribute('data-value',minvalue);
max.setAttribute('data-value',maxvalue);
var avgvalue = (minvalue + maxvalue)/2;
/* draw */
draw(slider,avgvalue);
}
var sliders = document.querySelectorAll('.min-max-slider');
sliders.forEach( function(slider) {
init(slider);
});
* {padding: 0; margin: 0;}
body {padding: 40px;}
.min-max-slider {position: relative; width: 200px; text-align: center; margin-bottom: 50px;}
.min-max-slider > label {display: none;}
span.value {height: 1.7em; font-weight: bold; display: inline-block;}
span.value.lower::before {content: "€"; display: inline-block;}
span.value.upper::before {content: "- €"; display: inline-block; margin-left: 0.4em;}
.min-max-slider > .legend {display: flex; justify-content: space-between;}
.min-max-slider > .legend > * {font-size: small; opacity: 0.25;}
.min-max-slider > input {cursor: pointer; position: absolute;}
/* webkit specific styling */
.min-max-slider > input {
-webkit-appearance: none;
outline: none!important;
background: transparent;
background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}
.min-max-slider > input::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 14px; /* Set a specific slider handle width */
height: 14px; /* Slider handle height */
background: #eee; /* Green background */
cursor: pointer; /* Cursor on hover */
border: 1px solid gray;
border-radius: 100%;
}
.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<div class="min-max-slider" data-legendnum="2">
<label for="min">Minimum price</label>
<input id="min" class="min" name="min" type="range" step="1" min="0" max="3000" />
<label for="max">Maximum price</label>
<input id="max" class="max" name="max" type="range" step="1" min="0" max="3000" />
</div>
Note that you should keep the step size to 1 to prevent the values to change due to redraws/redraw bugs.
View online at: https://codepen.io/joosts/pen/rNLdxvK
2022 - Accessible solution - 30 second solution to implement
This solution builds off of this answer by #JoostS. Accessibility is something none of the answers have focused on and that is a problem, so I built off of the above answer by making it more accessible & extensible since it had some flaws.
Usage is very simple:
Use the CDN or host the script locally: https://cdn.jsdelivr.net/gh/maxshuty/accessible-web-components/dist/simpleRange.min.js
Add this element to your template or HTML: <range-selector min-range="0" max-range="1000" />
Hook into it by listening for the range-changed event (or whatever event-name-to-emit-on-change you pass in)
That's it. View the full demo here. You can easily customize it by simply applying attributes like inputs-for-labels to use inputs instead of labels, slider-color to adjust the color, and so much more!
Here is a fiddle:
window.addEventListener('range-changed', (e) => {console.log(`Range changed for: ${e.detail.sliderId}. Min/Max range values are available in this object too`)})
<script src="https://cdn.jsdelivr.net/gh/maxshuty/accessible-web-components#latest/dist/simpleRange.min.js"></script>
<div>
<range-selector
id="rangeSelector1"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
number-of-legend-items-to-show="6"
/>
</div>
<div>
<range-selector
id="rangeSelector1"
min-label="Minimum"
max-label="Maximum"
min-range="1"
max-range="500"
number-of-legend-items-to-show="3"
inputs-for-labels
/>
</div>
<div>
<range-selector
id="rangeSelector2"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
number-of-legend-items-to-show="3"
slider-color="#6b5b95"
/>
</div>
<div>
<range-selector
id="rangeSelector3"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
hide-label
hide-legend
/>
</div>
I decided to address the issues of the linked answer like the labels using display: none (bad for a11y), no visual focus on the slider, etc., and improve the code by cleaning up event listeners and making it much more dynamic and extensible.
I created this tiny library with many options to customize colors, event names, easily hook into it, make the accessible labels i18n capable and much more. Here it is in a fiddle if you want to play around.
You can easily customize the number of legend items it shows, hide or show the labels and legend, and customize the colors of everything, including the focus color like this.
Example using several of the props:
<range-selector
min-label="i18n Minimum Range"
max-label="i18n Maximum Range"
min-range="5"
max-range="555"
number-of-legend-items-to-show="6"
event-name-to-emit-on-change="my-custom-range-changed-event"
slider-color="orange"
circle-color="#f7cac9"
circle-border-color="#083535"
circle-focus-border-color="#3ec400"
/>
Then in your script:
window.addEventListener('my-custom-range-changed-event', (e) => { const data = e.detail; });
Finally if you see that this is missing something that you need I made it very easy to customize this library.
Simply copy this file and at the top you can see cssHelpers and constants objects that contain most of the variables you would likely want to further customize.
Since I built this with a Native Web Component I have taken advantage of disconnectedCallback and other hooks to clean up event listeners and set things up.
Here is a reusable double range slider implementation, base on tutorial Double Range Slider by Coding Artist
near native UI, Chrome/Firefox/Safari compatible
API EventTarget based, with change/input events, minGap/maxGap properties
let $ = (s, c = document) => c.querySelector(s);
let $$ = (s, c = document) => Array.prototype.slice.call(c.querySelectorAll(s));
class DoubleRangeSlider extends EventTarget {
#minGap = 0;
#maxGap = Number.MAX_SAFE_INTEGER;
#inputs;
style = {
trackColor: '#dadae5',
rangeColor: '#3264fe',
};
constructor(container){
super();
let inputs = $$('input[type="range"]', container);
if(inputs.length !== 2){
throw new RangeError('2 range inputs expected');
}
let [input1, input2] = inputs;
if(input1.min >= input1.max || input2.min >= input2.max){
throw new RangeError('range min should be less than max');
}
if(input1.max > input2.max || input1.min > input2.min){
throw new RangeError('input1\'s max/min should not be greater than input2\'s max/min');
}
this.#inputs = inputs;
let sliderTrack = $('.slider-track', container);
let lastValue1 = input1.value;
input1.addEventListener('input', (e) => {
let value1 = +input1.value;
let value2 = +input2.value;
let minGap = this.#minGap;
let maxGap = this.#maxGap;
let gap = value2 - value1;
let newValue1 = value1;
if(gap < minGap){
newValue1 = value2 - minGap;
}else if(gap > maxGap){
newValue1 = value2 - maxGap;
}
input1.value = newValue1;
if(input1.value !== lastValue1){
lastValue1 = input1.value;
passEvent(e);
fillColor();
}
});
let lastValue2 = input2.value;
input2.addEventListener('input', (e) => {
let value1 = +input1.value;
let value2 = +input2.value;
let minGap = this.#minGap;
let maxGap = this.#maxGap;
let gap = value2 - value1;
let newValue2 = value2;
if(gap < minGap){
newValue2 = value1 + minGap;
}else if(gap > maxGap){
newValue2 = value1 + maxGap;
}
input2.value = newValue2;
if(input2.value !== lastValue2){
lastValue2 = input2.value;
passEvent(e);
fillColor();
}
});
let passEvent = (e) => {
this.dispatchEvent(new e.constructor(e.type, e));
};
input1.addEventListener('change', passEvent);
input2.addEventListener('change', passEvent);
let fillColor = () => {
let overallMax = +input2.max;
let overallMin = +input1.min;
let overallRange = overallMax - overallMin;
let left1 = ((input1.value - overallMin) / overallRange * 100) + '%';
let left2 = ((input2.value - overallMin) / overallRange * 100) + '%';
let {trackColor, rangeColor} = this.style;
sliderTrack.style.background = `linear-gradient(to right, ${trackColor} ${left1}, ${rangeColor} ${left1}, ${rangeColor} ${left2}, ${trackColor} ${left2})`;
};
let init = () => {
let overallMax = +input2.max;
let overallMin = +input1.min;
let overallRange = overallMax - overallMin;
let range1 = input1.max - overallMin;
let range2 = overallMax - input2.min;
input1.style.left = '0px';
input1.style.width = (range1 / overallRange * 100) + '%';
input2.style.right = '0px';
input2.style.width = (range2 / overallRange * 100) + '%';
fillColor();
};
init();
}
get minGap(){
return this.#minGap;
}
set minGap(v){
this.#minGap = v;
}
get maxGap(){
return this.#maxGap;
}
set maxGap(v){
this.#maxGap = v;
}
get values(){
return this.#inputs.map((el) => el.value);
}
set values(values){
if(values.length !== 2 || !values.every(isFinite))
throw new RangeError();
let [input1, input2] = this.#inputs;
let [value1, value2] = values;
if(value1 > input1.max || value1 < input1.min)
throw new RangeError('invalid value for input1');
if(value2 > input2.max || value2 < input2.min)
throw new RangeError('invalid value for input2');
input1.value = value1;
input2.value = value2;
}
get inputs(){
return this.#inputs;
}
get overallMin(){
return this.#inputs[0].min;
}
get overallMax(){
return this.#inputs[1].max;
}
}
function main(){
let container = $('.slider-container');
let slider = new DoubleRangeSlider(container);
slider.minGap = 30;
slider.maxGap = 70;
let inputs = $$('input[name="a"]');
let outputs = $$('output[name="a"]');
outputs[0].value = inputs[0].value;
outputs[1].value = inputs[1].value;
slider.addEventListener('input', (e) => {
let values = slider.values;
outputs[0].value = values[0];
outputs[1].value = values[1];
});
slider.addEventListener('change', (e) => {
let values = slider.values;
console.log('change', values);
outputs[0].value = values[0];
outputs[1].value = values[1];
});
}
document.addEventListener('DOMContentLoaded', main);
.slider-container {
display: inline-block;
position: relative;
width: 360px;
height: 28px;
}
.slider-track {
width: 100%;
height: 5px;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
border-radius: 5px;
}
.slider-container>input[type="range"] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
width: 100%;
outline: none;
background-color: transparent;
pointer-events: none;
}
.slider-container>input[type="range"]::-webkit-slider-runnable-track {
-webkit-appearance: none;
height: 5px;
}
.slider-container>input[type="range"]::-moz-range-track {
-moz-appearance: none;
height: 5px;
}
.slider-container>input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
margin-top: -9px;
height: 1.7em;
width: 1.7em;
background-color: #3264fe;
cursor: pointer;
pointer-events: auto;
border-radius: 50%;
}
.slider-container>input[type="range"]::-moz-range-thumb {
-moz-appearance: none;
height: 1.7em;
width: 1.7em;
cursor: pointer;
border: none;
border-radius: 50%;
background-color: #3264fe;
pointer-events: auto;
}
.slider-container>input[type="range"]:active::-webkit-slider-thumb {
background-color: #ffffff;
border: 3px solid #3264fe;
}
<h3>Double Range Slider, Reusable Edition</h3>
<div class="slider-container">
<div class="slider-track"></div>
<input type="range" name="a" min="-130" max="-30" step="1" value="-100" autocomplete="off" />
<input type="range" name="a" min="-60" max="0" step="2" value="-30" autocomplete="off" />
</div>
<div>
<output name="a"></output> ~ <output name="a"></output>
</div>
<pre>
Changes:
1. allow different min/max/step for two inputs
2. new property 'maxGap'
3. added events 'input'/'change'
4. dropped IE/OldEdge support
</pre>
For those working with Vue, there is now Veeno available, based on noUiSlider. But it does not seem to be maintained anymore. :-(
This code covers following points
Dual slider using HTML, CSS, JS
I have modified this slider using embedded ruby so we can save previously applied values using params in rails.
<% left_width = params[:min].nil? ? 0 : ((params[:min].to_f/100000) * 100).to_i %>
<% left_value = params[:min].nil? ? '0' : params[:min] %>
<% right_width = params[:max].nil? ? 100 : ((params[:max].to_f/100000) * 100).to_i %>
<% right_value = params[:max].nil? ? '100000' : params[:max] %>
<div class="range-slider-outer">
<div slider id="slider-distance">
<div class="slider-inner">
<div inverse-left style="width:<%= left_width %>%;"></div>
<div inverse-right style="width:<%= 100 - right_width %>%;"></div>
<div range style="left:<%= left_width %>%;right:<%= 100 - right_width %>%;"></div>
<span thumb style="left:<%= left_width %>%;"></span>
<span thumb style="left:<%= right_width %>%;"></span>
<div sign style="">
Rs.<span id="value"><%= left_value.to_i %></span> to
</div>
<div sign style="">
Rs.<span id="value"><%= right_value.to_i %></span>
</div>
</div>
<input type="range" name="min" value=<%= left_value %> max="100000" min="0" step="100" oninput="
this.value=Math.min(this.value,this.parentNode.childNodes[5].value-1);
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[1].style.width=value+'%';
children[5].style.left=value+'%';
children[7].style.left=value+'%';children[11].style.left=value+'%';
children[11].childNodes[1].innerHTML=this.value;" />
<input type="range" name="max" value=<%= right_value %> max="100000" min="0" step="100" oninput="
this.value=Math.max(this.value,this.parentNode.childNodes[3].value-(-1));
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[3].style.width=(100-value)+'%';
children[5].style.right=(100-value)+'%';
children[9].style.left=value+'%';children[13].style.left=value+'%';
children[13].childNodes[1].innerHTML=this.value;" />
</div>
<div class="range-label">
<div>0</div>
<div>100000</div>
</div>
</div>
[slider] {
/*width: 300px;*/
position: relative;
height: 5px;
/*margin: 20px auto;*/
/* height: 100%; */
}
[slider] > div {
position: absolute;
left: 13px;
right: 15px;
height: 14px;
top: 5px;
}
[slider] > div > [inverse-left] {
position: absolute;
left: 0;
height: 14px;
border-radius: 3px;
background-color: #CCC;
/*margin: 0 7px;*/
margin: 0 -7px;
}
[slider] > div > [inverse-right] {
position: absolute;
right: 0;
height: 14px;
border-radius: 3px;
background-color: #CCC;
/*margin: 0 7px;*/
margin: 0 -7px;
}
[slider] > div > [range] {
position: absolute;
left: 0;
height: 14px;
border-radius: 14px;
background-color:#8950fc;
}
[slider] > div > [thumb] {
position: absolute;
top: -3px;
z-index: 2;
height: 20px;
width: 20px;
text-align: left;
margin-left: -11px;
cursor: pointer;
/* box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4); */
background-color: #FFF;
/*border-radius: 50%;*/
border-radius:2px;
outline: none;
}
[slider] > input[type=range] {
position: absolute;
pointer-events: none;
-webkit-appearance: none;
z-index: 3;
height: 14px;
top: -2px;
width: 100%;
opacity: 0;
}
div[slider] > input[type=range]:focus::-webkit-slider-runnable-track {
background: transparent;
border: transparent;
}
div[slider] > input[type=range]:focus {
outline: none;
}
div[slider] > input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
-webkit-appearance: none;
}
div[slider] > input[type=range]::-ms-fill-lower {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-fill-upper {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-tooltip {
display: none;
}
[slider] > div > [sign] {
/* opacity: 0;
position: absolute;
margin-left: -11px;
top: -39px;
z-index:3;
background-color:#1a243a;
color: #fff;
width: 28px;
height: 28px;
border-radius: 28px;
-webkit-border-radius: 28px;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center;*/
color: #A5B2CB;
border-radius: 28px;
justify-content: center;
text-align: center;
display: inline-block;
margin-top: 12px;
font-size: 14px;
font-weight: bold;
}
.slider-inner{
text-align:center;
}
/*[slider] > div > [sign]:after {
position: absolute;
content: '';
left: 0;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top-width: 16px;
border-top-style: solid;
border-top-color:#1a243a;
}*/
[slider] > div > [sign] > span {
font-size: 12px;
font-weight: 700;
line-height: 28px;
}
[slider]:hover > div > [sign] {
opacity: 1;
}
.range-label{
display: flex;
justify-content: space-between;
margin-top: 28px;
padding: 0px 5px;
}
.range-slider-outer{
width:calc(100% - 20px);
margin:auto;
margin-bottom: 10px;
margin-top: 10px;
}

Use inline svg as background of the other element

How can I use #bg as a background for section?
https://jsfiddle.net/42nn4b49/2/
div {
width: 4em;
}
section {
outline: 1px dotted red;
width: 8em;
height: 8em;
background: url(#bg); /* Desn't work */
}
<div>
<svg viewBox="-4 -4 8 8" id="bg">
<circle r="4" />
</svg>
</div>
<section></section>
PS: Same question in russian
Originally, I'd hoped to be able to combine the results of Drawing an SVG file on a HTML5 canvas and How to dynamically change a class css styling?
However, it seems not to work - I've not been able to work out how to replace a background-image attrib of a rule. The console.log message is shown, but nothing changes.
So instead, I've opted just to create a rule that just sets the bkg img, and then appended it to the first style element found inside the head.
// useful for HtmlCollection, NodeList, String types
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
document.getElementById('mBtn').addEventListener('click', onBtnClicked, false);
}
function onBtnClicked(evt)
{
var svgElem = document.querySelector('svg');
var b64SVG = getDataURL2(svgElem);
// doesn't work
//alterExistingCSSRuleAttrib('#panel', 'background-image', 'url('+b64SVG+');' );
// does work
addCssRule('#panel', 'background-image', 'url('+b64SVG+')' );
// does work
alterExistingCSSRuleAttrib('#panel', 'width', "200px" );
// doesn't work
alterExistingCSSRuleAttrib('#panel', 'border', "solid 3px green" );
// does work
alterExistingCSSRuleAttrib('#panel', 'border-top-color', "green" );
alterExistingCSSRuleAttrib('#panel', 'border-top-width', "5px" );
}
function addCssRule(selectorText, attribName, value)
{
var style = document.querySelector('style');
style.innerHTML += selectorText + "{" + attribName + ": " + value + "; }";
}
function getDataURL2(svgElem)
{
var xml = new XMLSerializer().serializeToString(svgElem);
var svg64 = btoa(xml);
var b64Start = 'data:image/svg+xml;base64,';
return b64Start + svg64;
}
function alterExistingCSSRuleAttrib(selectorText, tgtAttribName, newValue)
{
var styleSheets = document.styleSheets;
forEach(styleSheets, styleSheetFunc);
function styleSheetFunc(CSSStyleSheet)
{
forEach(CSSStyleSheet.cssRules, cssRuleFunc);
}
function cssRuleFunc(rule)
{
if (selectorText.indexOf(rule.selectorText) != -1)
forEach(rule.style, cssRuleAttributeFunc);
function cssRuleAttributeFunc(attribName)
{
if (attribName == tgtAttribName)
{
rule.style[attribName] = newValue;
console.log('attribute replaced: '+attribName);
}
}
}
}
#panel
{
background: url(bluebombSmall.svg);
display: inline-block;
width: 100px;
height: 100px;
border: solid 1px red;
border-radius: 8px;
}
#bomb2
{
display: none;
}
<button id='mBtn'>Click to update/add styles</button><br>
<div id='panel'></div>
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" >
<g transform="translate(0,-1020.3622)">
<path d="m23.23,15.84a10.55,10.55,0,1,1,-21.11,0,10.55,10.55,0,1,1,21.11,0z" transform="matrix(1.1875635,0,0,1.1875635,0.68612298,1020.367)" fill="#26201e"/>
<path d="m23.23,15.84a10.55,10.55,0,1,1,-21.11,0,10.55,10.55,0,1,1,21.11,0z" transform="matrix(0.86603158,0,0,0.86603158,2.4299747,1024.1874)" fill="#333"/>
<path d="m-13.04,19.32a1.964,1.964,0,1,1,-3.929,0,1.964,1.964,0,1,1,3.929,0z" transform="matrix(1.924285,1.1058108,-1.1908732,2.0723069,62.314757,1012.6494)" fill="#CCC"/>
<path d="m15.69,1026c0.02518-5.037,7.647-7.396,8.907-2.969,0.7936,2.761,1.349,5.666,4.877,6.786" stroke="#888" stroke-width="1.5px" fill="none"/>
<rect height="2.399" width="4.798" y="1026" x="13.31" stroke-width="0" fill="#26201e"/>
<path fill="#F00" transform="translate(2.0203051,1022.13)" d="M29.8,10.53,27.1,9.62,24.82,11.32,24.86,8.477,22.54,6.833,25.25,5.989,26.1,3.271,27.74,5.595,30.59,5.558,28.89,7.839z"/>
</g>
</svg>
quick jsfiddle I made for you: https://jsfiddle.net/42nn4b49/7/
HTML:
<div>
<svg viewBox="-4 -4 8 8" class="bg">
<circle r="4" />
</svg>
<section>Test</section>
</div>
CSS:
div {
width: 4em;
}
.bg {
position: absolute;
top: 0;
width: 8em;
height: 8em;
z-index: -100;
}
section {
outline: 1px dotted red;
width: 8em;
height: 8em;
background: url(#bg);
color: #fff;
text-align: center;
}
I assume that it's the result of absolute positioning your after. Essentially as #luuk mentioned, all you need is to wrap both elements in a single container, and set section { position : absolute;}. to understand this further read up here.
#wrap {
position: relative;
width: 30%;
background: #eee;
padding: .5em;
}
section {
position: absolute;
color: #f8f8ff;
top: 0;
left: 0;
z-index: 2;
outline: 1px dotted red;
width: 100%;
text-align: center;
}
#circle {
position: relative;
width: 100%;
z-index: 1;
}
p {
padding: 1.5em;
}
<div id="wrap">
<div id="circle">
<svg viewBox="-4 -4 8 8" id="bg">
<circle r="4" />
</svg>
</div>
<section id="box">
<p>This is my content.</p>
</section>
</div>
Feel free to ask any further questions.

CSS animated slide down when lower element gets removed (elements in container with relative position)

I've got a script which create a simple container for error message boxes and if needed create these boxes in it. The container got a fixed width, like the boxes but not a fixed height, so the boxes stack about each other in a vertical row. Also the boxes in the container got position: relative.
Picture of the container & boxes:
Now what I want is, that if you remove a box, the above boxes slides down, animated. I tried it with CSS transitions but it didn't work (I'm pretty new to transitions, so I don't even know if it's possible with position: relative because the top and bottom properties aren't set).
Example [See picture for reference]: I click on box 3, box 3 gets removed, box 1 and 2 slide down next to 4, 5, and 6. This works, but it isn't animated yet.
Question: Is it possible to do this with CSS, and if so, how?
HTML:
<div class="popBoxContainer popBoxContainerRight">
<div class="popBox error" onclick="this.parentNode.removeChild(this);" title="click to close">
<p class="popBox__title">error</p>
<span class="popBox__msg">Test-Box-1</span>
</div>
<div class="popBox error" onclick="this.parentNode.removeChild(this);" title="click to close">
<p class="popBox__title">error</p>
<span class="popBox__msg">Test-Box-2</span>
</div>
<div class="popBox error" onclick="this.parentNode.removeChild(this);" title="click to close">
<p class="popBox__title">error</p>
<span class="popBox__msg">Test-Box-3</span>
</div>
</div>
CSS:
.popBoxContainer {
position: fixed;
width: 300px;
bottom: 0;
padding: 0 10px 15px 10px;
}
.popBoxContainerRight {
right: 0;
}
.popBoxContainerLeft {
left: 0;
}
.popBox {
padding: 15px 25px;
margin-top: 20px;
background-color: #fff;
box-shadow: 2px 2px 3px rgba(0,0,0,.03);
opacity: 0.8;
}
.popBox:hover {
opacity: 1;
cursor: pointer;
}
.popBox .popBox__title {
font-family: 'Oswald', sans-serif;
font-size: 18px;
margin-bottom: 8px;
text-transform: uppercase;
}
.popBox.error .popBox__title {
color: #FF5252;
}
.popBox.warning .popBox__title {
color: #FFDB70;
}
.popBox .popBox__msg {
color: #4c4c4c;
}
JS:
function initPopBox(side) {
if(document.getElementsByClassName('popBoxContainer').length < 1) {
var box = document.createElement('div');
box.className = 'popBoxContainer';
if(side != "") {
if(side == "left") {
box.className += ' popBoxContainerLeft';
} else {
box.className += ' popBoxContainerRight';
}
} else {
box.className += ' popBoxContainerRight';
}
document.body.appendChild(box);
}
}
function popBox(type, txt) {
var boxCon = document.getElementsByClassName('popBoxContainer');
if(boxCon.length < 1) {
initPopBox();
}
switch(type) {
case 'error':
boxCon[0].innerHTML += '<div class="popBox error" onclick="this.parentNode.removeChild(this);" title="click to close"><p class="popBox__title">error</p><span class="popBox__msg">'+txt+'</span></div>';
break;
case 'warning':
boxCon[0].innerHTML += '<div class="popBox warning" onclick="this.parentNode.removeChild(this);" title="click to close"><p class="popBox__title">warning</p><span class="popBox__msg">'+txt+'</span></div>';
break;
default:
boxCon[0].innerHTML += '<div class="popBox error" onclick="this.parentNode.removeChild(this);" title="click to close"><p class="popBox__title">error</p><span class="popBox__msg">'+txt+'</span></div>';
break;
}
}
Without knowing your HTML it's hard to really solve. That being said, you can't get the desired behavior using only CSS, like Paulie_D said in the comments above. You can get close (if you don't need a sliding animation).
Here is a combination of CSS and jQuery that does what you want:
$("label").click(function() {
$(this).delay(600).queue(function (next) {
$(this).css('display', 'none');
next();
});
});
body {
margin: 0;
}
input[type="checkbox"] {
position: absolute;
left: -9999px;
}
.container {
width: 235px;
margin: 0 auto;
background: lightgrey;
padding: 1px 0;
}
label {
display: block;
background: white;
margin: 16px;
width: 200px;
height: 50px;
color: #c95959;
position: relative;
}
p {
margin-top: 12px;
color: #3e3b3b;
}
#box1:checked ~ .container .label1, #box2:checked ~ .container .label2, #box3:checked ~ .container .label3, #box4:checked ~ .container .label4, #box5:checked ~ .container .label5, #box6:checked ~ .container .label6 {
opacity: 0;
transition: opacity 0.7s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input type="checkbox" id="box1"/>
<input type="checkbox" id="box2"/>
<input type="checkbox" id="box3"/>
<input type="checkbox" id="box4"/>
<input type="checkbox" id="box5"/>
<input type="checkbox" id="box6"/>
<div class="container">
<label class="label1" for="box1">
<span>ERROR</span>
<p>Test-Error-Box-1</p>
</label>
<label class="label2" for="box2">
<span>ERROR</span>
<p>Test-Error-Box-2</p>
</label>
<label class="label3" for="box3">
<span>ERROR</span>
<p>Test-Error-Box-3</p>
</label>
<label class="label4" for="box4">
<span>ERROR</span>
<p>Test-Error-Box-4</p>
</label>
<label class="label5" for="box5">
<span>ERROR</span>
<p>Test-Error-Box-5</p>
</label>
<label class="label6" for="box6">
<span>ERROR</span>
<p>Test-Error-Box-6</p>
</label>
</div>
JSFIDDLE Example
You need to use max-height here. On the original element have a max-height of whatever height you want it to be. Using height auto I don't think will work with this. There's a good example at: http://codepen.io/LFeh/pen/ICkwe
Take a look at this code:
.element{
-webskit-transition: max-height 1s;
max-height:0px;
}
.parentelement:hover childelement {
max-height:50px;
}