I have a two thumb range slider to set the max value and the min value, now I realized that it's possible to go over the thumbs - max can go over min and min over max.
I want to limit min to not go over max and max not over min
After some researched I've not seen any usefull JS.
Is there any option I can use for this one.
const twoRangeSlider = (() => {
const rangeCheck = (rangeInputs, rangeMinOutput, rangeMaxOutput) => {
const rangeMin = rangeInputs[0].min ? rangeInputs[0].min : 0;
const rangeMax = rangeInputs[0].max ? rangeInputs[0].max : 100;
let rangeMinValue = parseFloat(rangeInputs[1].value);
let rangeMaxValue = parseFloat(rangeInputs[0].value);
rangeMinValue = Math.min(Math.max(rangeMinValue, rangeMin), rangeMaxValue);
const rangeMinPercentage = Number(
((rangeMinValue - rangeMin) * 100) / (rangeMax - rangeMin));
const rangeMaxPercentage = Number(
((rangeMaxValue - rangeMin) * 100) / (rangeMax - rangeMin));
rangeInputs[0].style.background = `linear-gradient(to right, #000 ${rangeMaxPercentage}%, #c4c8ca ${rangeMaxPercentage}%)`;
rangeInputs[1].style.background = `linear-gradient(to right,#c4c8ca ${rangeMinPercentage}%, transparent ${rangeMinPercentage}%)`;
};
const bindComponent = (item) => {
const rangeInputs = item.querySelectorAll('.js-two-range-slider-input');
const rangeMinOutput = item.querySelector('.js-two-range-slider-min-value');
const rangeMaxOutput = item.querySelector('.js-two-range-slider-max-value');
item.addEventListener("input", () => {
rangeCheck(rangeInputs, rangeMinOutput, rangeMaxOutput);
});
rangeCheck(rangeInputs, rangeMinOutput, rangeMaxOutput);
};
const init = () => {
const rootEl = document.getElementsByClassName("js-two-range-slider");
[...rootEl].forEach((item) => bindComponent(item));
};
return {
init
};
})();
twoRangeSlider.init();
.two-range-slider {
position: relative;
height: 4px;
width: 164px;
margin-bottom: 50px;
}
.two-range-slider__input {
position: absolute;
left: 40%;
top: 15px;
box-shadow: 0;
appearance: none;
width: 60%;
height: 3px;
border-radius: 50px;
background-color: #000;
outline: 0;
}
.two-range-slider__value {
padding: 0px 10px;
color: #000;
position: relative;
top: 19px;
outline: none;
width: 50px;
position: absolute;
border: 1px solid #ccc;
box-sizing: border-box;
}
.two-range-slider__input::-webkit-slider-thumb {
z-index: 2;
position: relative;
-webkit-appearance: none;
appearance: none;
height: 13px;
width: 13px;
border-radius: 50%;
background: #000;
cursor: pointer;
}
.two-range-slider__input::-moz-range-thumb {
z-index: 2;
position: relative;
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
border: 0;
background: #FFFFFF;
cursor: pointer;
}
.two-range-slider__output {
display: inline-flex;
flex-flow: row nowrap;
justify-content: space-between;
width: 140%;
top: -15px;
margin-left: 0px;
color: #000;
position: relative;
}
.range-slider__value {
padding: 0px 10px;
color: #000;
outline: none;
width: 50px;
}
<div class="two-range-slider js-two-range-slider">
<input type="range" value="80" min="10" max="100" step="1" class="two-range-slider__input js-two-range-slider-input">
<input type="range" value="30" min="10" max="100" step="1" class="two-range-slider__input js-two-range-slider-input">
<div class="two-range-slider__output">
<p class="minmax">Min</p><input class="two-range-slider__value js-two-range-slider-min-value" type="number" value="30" min="10" max="100" step="1"></input>
<p class="maxmin">Max</p><input style="right: -5px;" class="two-range-slider__value js-two-range-slider-max-value" type="number" value="80" min="10" max="100" step="1"></input>
</div>
</div>
I shortened your example a bit to show what i meant in my comment.
const twoRangeSlider = (() => {
const bindComponent = (item) => {
const rangeInputs = item.querySelectorAll('.js-two-range-slider-input');
item.addEventListener("input", ({ target }) => {
const [ right, left ] = rangeInputs;
if (target == right) {
left.value = Math.min(+right.value -1, +left.value);
} else {
right.value = Math.max(+left.value +1, +right.value);
}
});
};
const init = () => {
const rootEl = document.getElementsByClassName("js-two-range-slider");
[...rootEl].forEach((item) => bindComponent(item));
};
return {
init
};
})();
twoRangeSlider.init();
.two-range-slider {
position: relative;
height: 4px;
width: 164px;
margin-bottom: 50px;
}
.two-range-slider__input {
position: absolute;
left: 40%;
top: 15px;
box-shadow: 0;
appearance: none;
width: 60%;
height: 3px;
border-radius: 50px;
background-color: #000;
outline: 0;
}
.two-range-slider__value {
padding: 0px 10px;
color: #000;
position: relative;
top: 19px;
outline: none;
width: 50px;
position: absolute;
border: 1px solid #ccc;
box-sizing: border-box;
}
.two-range-slider__input::-webkit-slider-thumb {
z-index: 2;
position: relative;
-webkit-appearance: none;
appearance: none;
height: 13px;
width: 13px;
border-radius: 50%;
background: #000;
cursor: pointer;
}
.two-range-slider__input::-moz-range-thumb {
z-index: 2;
position: relative;
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
border: 0;
background: #FFFFFF;
cursor: pointer;
}
.two-range-slider__output {
display: inline-flex;
flex-flow: row nowrap;
justify-content: space-between;
width: 140%;
top: -15px;
margin-left: 0px;
color: #000;
position: relative;
}
.range-slider__value {
padding: 0px 10px;
color: #000;
outline: none;
width: 50px;
}
<div class="two-range-slider js-two-range-slider">
<input type="range" value="80" min="10" max="100" step="1" class="two-range-slider__input js-two-range-slider-input">
<input type="range" value="30" min="10" max="100" step="1" class="two-range-slider__input js-two-range-slider-input">
<div class="two-range-slider__output">
<p class="minmax">Min</p><input class="two-range-slider__value js-two-range-slider-min-value" type="number" value="30" min="10" max="100" step="1">
<p class="maxmin">Max</p><input style="right: -5px;" class="two-range-slider__value js-two-range-slider-max-value" type="number" value="80" min="10" max="100" step="1">
</div>
</div>
Related
I have multiple html5 range sliders on my page, this was working for one however I can't make this work for seperate sliders. They should have no relation, so the user can select a score on each different question.
I know the jQuery code should target the slider, however I cannot get this to work. We are going to be using around 20 of these sliders, so need this to be dynamic really.
var sheet = document.createElement('style'),
$rangeInput = $('.range input'),
prefs = ['webkit-slider-runnable-track', 'moz-range-track', 'ms-track'];
document.body.appendChild(sheet);
var getTrackStyle = function(el) {
var curVal = el.value,
val = (curVal - 1) * 25,
style = '';
// Set active label
$('.range-labels li').removeClass('active selected');
var curLabel = $('.range-labels').find('li:nth-child(' + curVal + ')');
curLabel.addClass('active selected');
curLabel.prevAll().addClass('selected');
//curLabel.addClass('halfselect');
// Change background gradient
for (var i = 0; i < prefs.length; i++) {
style += '.range {background: linear-gradient(to right, #a50e2d 0%, #a50e2d ' + val + '%, #fff ' + val + '%, #fff 100%)}';
style += '.range input::-' + prefs[i] + '{background: linear-gradient(to right, #a50e2d 0%, #a50e2d ' + val + '%, #b2b2b2 ' + val + '%, #b2b2b2 100%)}';
}
return style;
}
$rangeInput.on('input', function() {
sheet.textContent = getTrackStyle(this);
});
// Change input value on label click
$('.range-labels li').on('click', function() {
var index = $(this).index();
$rangeInput.val(index + 1).trigger('input');
});
body {
padding: 100px;
}
.range {
position: relative;
width: 100%;
height: 5px;
}
.range input {
width: 100%;
position: absolute;
top: 2px;
height: 0;
-webkit-appearance: none;
}
.range input::-webkit-slider-thumb {
-webkit-appearance: none;
width: 30px;
height: 30px;
margin: -16px 0 0 0;
border-radius: 50%;
background: #a50e2d;
cursor: pointer;
border: 0 !important;
}
/*
.range input::-moz-range-thumb {
width: 30px;
height: 30px;
margin: -8px 0 0;
border-radius: 50%;
background: #a50e2d;
cursor: pointer;
border: 0 !important;
}
.range input::-ms-thumb {
width: 30px;
height: 30px;
margin: -8px 0 0;
border-radius: 50%;
background: #a50e2d;
cursor: pointer;
border: 0 !important;
}
*/
.range input::-webkit-slider-runnable-track {
width: 100%;
height: 2px;
cursor: pointer;
background: #272725;
}
.range input::-moz-range-track {
width: 100%;
height: 2px;
cursor: pointer;
background: #272725;
}
.range input::-ms-track {
width: 100%;
height: 2px;
cursor: pointer;
background: #272725;
}
.range input:focus {
background: none;
outline: none;
}
.range input::-ms-track {
width: 100%;
cursor: pointer;
background: transparent;
border-color: transparent;
color: transparent;
}
.range-labels {
margin: 18px -11% 0;
padding: 0;
list-style: none;
display: flex;
position: relative;
}
.range-labels li {
position: relative;
text-align: center;
color: #272725;
font-size: 14px;
cursor: pointer;
flex: 1;
}
.range-labels li::before,
.halfselect::before {
position: absolute;
top: -37px;
right: 0;
left: 0;
content: "";
margin: 0 auto;
width: 30px;
height: 30px;
background: #272725;
border-radius: 50%;
}
.range-labels .active {
color: #a50e2d;
}
.range-labels .selected::before,
.range-labels .active::before {
background: #a50e2d;
}
.range-labels .active.selected::before {
display: none;
}
.halfselect {}
.halfselect:after {
position: absolute;
top: -23px;
right: auto;
left: 0;
content: "";
margin: 0 auto;
width: 50%;
height: 5px;
background: #a50e2d;
z-index: 9999;
}
span.question {
color: #272725 !important;
padding: 5px 0;
display: block;
width: 70%;
margin: auto;
}
span.inp-value {
position: absolute;
top: -60px;
display: block;
text-align: center;
width: 100%;
color: #272725;
}
<div class="questions">
<div class="range">
<input type="range" min="1" max="5" steps="1" value="1">
</div>
<ul class="range-labels" id="working-with-people-question1">
<li id="1" class="active selected"><span class="inp-value">1</span><span class="question">Shows little interest in or understanding of others</span></li>
<li id="2"><span class="inp-value">2</span></li>
<li id="3"><span class="inp-value">3</span></li>
<li id="4"><span class="inp-value">4</span></li>
<li id="5"><span class="inp-value">5</span><span class="question">Demonstrate an interest in and understanding of others</span></li>
</ul>
</div>
<div class="questions" style="padding-top: 100px">
<div class="range">
<input type="range" min="1" max="5" steps="1" value="1">
</div>
<ul class="range-labels" id="working-with-people-question1">
<li id="1" class="active selected"><span class="inp-value">1</span><span class="question">Question 2</span></li>
<li id="2"><span class="inp-value">2</span></li>
<li id="3"><span class="inp-value">3</span></li>
<li id="4"><span class="inp-value">4</span></li>
<li id="5"><span class="inp-value">5</span><span class="question">Question 2</span></li>
</ul>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
Codepen here - https://codepen.io/scottYg55/pen/BazGamz
The problem is that getTrackStyle(this) is sending all the elements and not just (this). (this) is global and not set to the individual (this) function
I have created a custom slider. I want to make the position of the marker change as I drag the slider handle. However the gap between the marker and the handle is getting wider as the value gets bigger.
I think the problem is to do with using margin-left/left or absolute/relative positioning.
Can anyone help me to figure out if the concept of the position of mine is wrong?
$(function() {
$('.h-rs-line').on('input', function() {
var val = $(this).val();
var min = $(this).attr('min');
var max = $(this).attr('max');
var portion = (val - min) / (max - min);
$('.h-rs-indicator').text(val);
$('.h-rs-indicator').css('left', portion * $('.h-rs-line').width());
});
});
body {
background: #000;
}
.h-rs {
position: relative;
}
.h-rs-index {
color: #FFF;
}
.h-rs-container {
display: flex;
flex-direction: column;
margin: 32px;
}
.h-rs-indicator {
display: block;
width: 50px;
height: 35px;
border-radius: 12px;
text-align: center;
line-height: 35px;
background: #FFF;
position: absolute;
top: -30px;
left: 0;
margin-left: -16px;
}
.h-rs-indicator::after {
content: '';
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 9px solid #FFF;
position: absolute;
top: 98%;
left: 50%;
margin-left: -8px;
}
.h-rs-line {
width: 300px;
margin-top: 24px;
-webkit-appearance: none;
border-radius: 24px;
}
.h-rs-line::-webkit-slider-runnable-track {
-webkit-appearance: none;
height: 7px;
border-radius: 24px;
}
.h-rs-line::-webkit-slider-thumb {
-webkit-appearance: none;
background: #FFF;
height: 18px;
width: 18px;
border-radius: 100%;
box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25);
margin-top: -5.5px;
}
.h-rs-index {
display: flex;
margin-top: 9px;
width: 300px;
justify-content: space-between;
}
.h-rs-index span:first-child {
margin-left: 6.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="h-rs-container">
<div class="h-rs">
<span class="h-rs-indicator">0<span></span></span>
<input class="h-rs-line" type="range" value="0" min="0" max="100" />
</div>
<div class="h-rs-index">
<span>0</span><span>100</span>
</div>
</div>
Compensate for the thumb width
You forgot to compenstate for the size of the range slider thumb. The slider thumb can only travel the width of the slider minus the size of the thumb (so 300px minus ~18px). Because the thumb can only travel 282px, so should the label, or they will be out of sync.
Therefore this:
$('.h-rs-indicator').css('left', portion * $('.h-rs-line').width());
... should become this:
$('.h-rs-indicator').css('left', portion * ($('.h-rs-line').width() - 18));
... where 18 is the width of the range slider thumb in pixels.
$(function() {
$('.h-rs-line').on('input', function() {
var val = $(this).val();
var min = $(this).attr('min');
var max = $(this).attr('max');
var portion = (val - min) / (max - min);
$('.h-rs-indicator').text(val);
$('.h-rs-indicator').css('left', portion * ($('.h-rs-line').width() - 18));
});
});
body {
background: #000;
}
.h-rs {
position: relative;
}
.h-rs-index {
color: #FFF;
}
.h-rs-container {
display: flex;
flex-direction: column;
margin: 32px;
}
.h-rs-indicator {
display: block;
width: 50px;
height: 35px;
border-radius: 12px;
text-align: center;
line-height: 35px;
background: #FFF;
position: absolute;
top: -30px;
left: 0;
margin-left: -16px;
}
.h-rs-indicator::after {
content: '';
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 9px solid #FFF;
position: absolute;
top: 98%;
left: 50%;
margin-left: -8px;
}
.h-rs-line {
width: 300px;
margin-top: 24px;
-webkit-appearance: none;
border-radius: 24px;
}
.h-rs-line::-webkit-slider-runnable-track {
-webkit-appearance: none;
height: 7px;
border-radius: 24px;
}
.h-rs-line::-webkit-slider-thumb {
-webkit-appearance: none;
background: #FFF;
height: 18px;
width: 18px;
border-radius: 100%;
box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25);
margin-top: -5.5px;
}
.h-rs-index {
display: flex;
margin-top: 9px;
width: 300px;
justify-content: space-between;
}
.h-rs-index span:first-child {
margin-left: 6.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="h-rs-container">
<div class="h-rs">
<span class="h-rs-indicator">0<span></span></span>
<input class="h-rs-line" type="range" value="0" min="0" max="100" />
</div>
<div class="h-rs-index">
<span>0</span><span>100</span>
</div>
</div>
Cross-browser
Getting your range slider to look the same (and being the same size) in every browser involves a lot of CSS. This is explained very well in this article on CSS tricks: https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/
Non-jQuery
How about a rewrite from jQuery to non-jQuery? That could be much lighter... Something like this:
<input type="range" min="0" max="100" oninput="updateRangeSlider(this)" onchange="updateRangeSlider(this)" />
With this function in the footer:
function updateRangeSlider(el){
var val = el.value;
var min = el.getAttribute('min');
var max = el.getAttribute('max');
var portion = (val - min) / (max - min);
var iel = el.parentNode.querySelector('.h-rs-indicator');
iel.innerHTML = val + '<span></span>';
iel.style.left = (portion * (el.offsetWidth-18)) + 'px';
}
Note that this function is reusable.
I am a stuck on following issues:
How to add required error just once after first click on submit button? And not at any subsequent click after.
How can to start function (checkValid()) with RegExp validation only after first function (checkRequired()) implementation with required checking?
How to show every error after RegExp validation in its relative element? Now all errors are displayed in the last block with phone input.
Here is case on jsfiddle:
https://jsfiddle.net/SanchezMalina/hno7v4tf/35/
Or code here:
// regexp pattern functions
function validateEmail(email) {
let re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email.toLowerCase());
}
function validatePhone(phone) {
let re = /^[0-9]{7,20}$/;
return re.test(phone.toLowerCase());
}
function validateName(name) {
let re = /^([a-zA-Z0-9А]){3,50}$/;
return re.test(name.toLowerCase());
}
let flag = false;
// check required fields
function checkRequired() {
let notFilled = document.querySelectorAll('.is-required');
notFilled.forEach(function (el) {
if (el.value === '') {
el.parentElement.classList.add('is-empty');
addRequiredError();
} else {
el.parentElement.classList.remove('is-empty');
removeRequiredError();
}
});
}
let formFields = document.querySelectorAll('.form__field');
//add required error message
function addRequiredError() {
let errorRequiredMsg = document.createElement('div');
errorRequiredMsg.classList.add('input-required');
errorRequiredMsg.innerHTML = `<span> This field is required! </span>`;
formFields.forEach( function (elem) {
if (elem.classList.contains('is-empty')) {
elem.appendChild(errorRequiredMsg);
}
});
}
//remove required error message
function removeRequiredError() {
let requiredErrorMsg = document.querySelectorAll('.form__field .input-required');
requiredErrorMsg.forEach(function (elem) {
elem.parentElement.removeChild(elem);
});
flag = true;
}
//check validation inputs
function checkValid() {
let firstName = document.querySelector('#f-name');
let lastName = document.querySelector('#l-name');
let selectCountry = document.querySelector('.form__select');
let phone = document.querySelector('#cell');
let email = document.querySelector('#email');
formFields.forEach(function () {
if(!validateName(firstName.value) && !validateName(lastName.value) && !validatePhone(phone.value) && !validateEmail(email.value)) {
firstName.parentElement.classList.add('is-error');
lastName.parentElement.classList.add('is-error');
selectCountry.parentElement.classList.add('is-error');
phone.parentElement.classList.add('is-error');
email.parentElement.classList.add('is-error');
addValidError();
} else {
firstName.parentElement.classList.remove('is-error');
lastName.parentElement.classList.remove('is-error');
selectCountry.parentElement.classList.remove('is-error');
phone.parentElement.classList.remove('is-error');
email.parentElement.classList.remove('is-error');
removeValidError();
}
});
}
//add invalid error message
function addValidError() {
let errorValidMsg = document.createElement('div');
errorValidMsg.classList.add('input-error');
errorValidMsg.innerHTML = `<span> Input is invalid! </span>`;
formFields.forEach(function (elem) {
if (elem.classList.contains('is-error')) {
elem.appendChild(errorValidMsg);
}
});
// for (let i = 0; i < formFields.length; i++) {
//
// if (formFields[i].classList.contains('is-error')) {
//
// formFields[i].appendChild(errorValidMsg);
//
// }
// }
}
//remove invalid error message
function removeValidError() {
let requiredErrorMsg = document.querySelectorAll('.input-error');
requiredErrorMsg.forEach(function (elem) {
elem.parentNode.removeChild(elem);
})
}
//form submit handler
let formTrial = document.querySelector('.form__main');
formTrial.addEventListener('submit', function (event) {
event.preventDefault();
checkRequired();
console.log(flag);
if(flag !== false) {
checkValid();
}
});
.form__main {
display: block;
margin: 25px auto;
width: 450px;
}
.form__field {
position: relative;
margin: 10px 0;
padding: 5px 15px;
background-color: #fff;
height: 50px;
}
.form__field_select::after {
content: "";
width: 0;
height: 0;
border: 7px solid transparent;
border-color: #000 transparent transparent transparent;
position: absolute;
top: 55%;
right: 17px;
transform: translateY(-50%);
}
.form__input {
position: absolute;
left: 0;
top: 10px;
right: 0;
bottom: 10px;
padding-left: 45px;
background: none;
font-size: 14px;
font-weight: 400;
font-family: "Open Sans", sans-serif;
width: 100%;
color: #333;
line-height: 1.714;
box-sizing: border-box;
}
.form__input:focus ~ .form__label,
.form__input:valid ~ .form__label {
top: 0;
left: 45px;
transform: scale(0.6, 0.6);
color: #000;
}
.form__input:focus ~ .form__label[for=cell],
.form__input:valid ~ .form__label[for=cell] {
top: 0;
left: 125px;
}
.form__input[type=tel] {
padding-left: 125px;
}
.form__input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 100px white inset;
}
.form__label {
position: absolute;
top: 50%;
left: 50px;
transform-origin: left top;
transition: all 0.3s ease;
transform: translateY(-50%);
font-size: 14px;
font-family: "Open Sans", sans-serif;
color: #999999;
line-height: 1.714;
}
.form__label[for=cell] {
left: 135px;
}
.form__select {
position: absolute;
width: 100%;
height: 100%;
top: 50%;
transform: translateY(-50%);
left: 0;
right: 0;
padding-left: 50px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
background-color: transparent;
border-color: #fff;
font-size: 14px;
font-family: "Open Sans", sans-serif;
color: #000;
line-height: 1.714;
}
.form__country-code {
position: absolute;
color: #000;
left: 50px;
font-size: 14px;
font-family: "Open Sans", sans-serif;
line-height: 2;
height: 100%;
top: 0;
bottom: 0;
display: flex;
align-items: center;
border-right: 1px solid;
padding-right: 20px;
z-index: 2;
}
.form .btn {
width: 100%;
margin: 20px 0;
}
.form .btn:hover {
transform: translateY(-5px);
box-shadow: 1px 3px 20px #c5f833;
}
<form method="post" class="form__main" novalidate>
<div class="form__field">
<i class="icon icon-user"></i>
<input class="form__input is-required" id="f-name" type="text" required>
<label class="form__label" for="f-name">First Name</label>
</div>
<div class="form__field">
<i class="icon icon-user"></i>
<input class="form__input is-required" id="l-name" type="text" required>
<label class="form__label" for="l-name">Last Name</label>
</div>
<div class="form__field">
<i class="icon icon-envelop"></i>
<input class="form__input" id="email" type="text" required>
<label class="form__label" for="email">Email</label>
</div>
<div class="form__field form__field_select">
<i class="icon icon-pin"></i>
<select class="form__select" name="country">
<option value="ro" selected>USA</option>
<option value="ua">India</option>
<option value="il">Spain</option>
</select>
</div>
<div class="form__field">
<i class="icon icon-phone"></i>
<span class="form__country-code">+10000</span>
<input class="form__input" id="cell" type="tel" required>
<label class="form__label" for="cell">Phone</label>
</div>
<button class="btn btn_lime">Try for free</button>
</form>
1)
if( !el.parentElement.classList.contains('is-empty') ) {
addRequiredError();
}
2)
not sure to understand the question
3)
function addValidError() {
let errorValidMsg = document.createElement('div');
errorValidMsg.classList.add('input-error');
errorValidMsg.innerHTML = '<span> Input is invalid! </span>';
formFields.forEach(function (elem) { // <-- here formFields is not defined
}
https://gyazo.com/aa49eb6d6849b3adabb8924aa9e40594
I have the elements in the diagram and I want to let the user choose in which element they are going to add the semi column to add text, but I dont know how to let them select a element and then add according to that selection.
var addDetail = document.getElementById('addDetail');
var clauseInput = document.getElementsByClassName('clause');
document.addEventListener('click', function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if(target == clauseInput[0] || target == clauseInput[1] ||
target == clauseInput[2] || target == clauseInput[3] ||
target == clauseInput[4] || target == addDetail) {
document.getElementById('addDetail').style.display='inline-block';
// createDetail();
console.log(target);
} else {
document.getElementById('addDetail').style.display='none';
}
}, false);
Update
Instead of searching through all clause elements every time when addDetail been clicked, there is e.relatedTarget that really suitable to your problem, detailed documentation, and the update snippet :
/*CREATE TOP AND BOTTOM CLAUSES*/
/*Top Clauses*/
const addClauseTop = document.querySelector('#addClauseTop');
var targetClauseElement;
var addDetail = document.getElementById('addDetail');
addDetail.addEventListener('focusin', function(e) {
createDetail(e.relatedTarget);
});
window.addEventListener('mouseup',function(e) {
if ( e.currentTarget != addDetail ) {
addDetail.style.display='none';
}
});
var firstTopClause = document.getElementsByClassName('clause')[0];
firstTopClause.addEventListener('click', function(e) {
addDetail.style.display='inline-block';
});
var firstBottomClause = document.getElementsByClassName('clauseDivReverse')[0];
firstBottomClause.addEventListener('click', function(e) {
addDetail.style.display='inline-block';
});
addClauseTop.addEventListener('click',function(e){
e.preventDefault();
//Get Divs-Overlay
const topDivs = document.querySelector('#topClauses');
const bottomDivs = document.querySelector('#bottomClauses');
// Create Elements
const clauseDiv = document.createElement('div');
const clauseText = document.createElement('input');
const clauseStroke = document.createElement('div');
// // //Give Style
clauseDiv.classList.add('clauseDiv');
clauseDiv.addEventListener('click', function(e) {
addDetail.style.display='inline-block';
});
clauseText.classList.add('clause');
clauseText.setAttribute("id", "clause");
clauseStroke.classList.add('strokeClause');
//
// // Append to document
clauseDiv.appendChild(clauseText);
clauseDiv.appendChild(clauseStroke);
topDivs.appendChild(clauseDiv);
document.body.appendChild(topDivs);
document.body.appendChild(bottomDivs);
})
/*BOTTOM Clauses*/
const addClauseBottom = document.querySelector('#addClauseBottom');
addClauseBottom.addEventListener('click',function(e){
e.preventDefault();
//Get Divs-Overlay
const topDivs = document.querySelector('#topClauses');
const bottomDivs = document.querySelector('#bottomClauses');
// Create Elements
const clauseDiv = document.createElement('div');
clauseDiv.addEventListener('click', function(e) {
targetClauseElement = e.currentTarget;
addDetail.style.display='inline-block';
});
const clauseText = document.createElement('input');
const clauseStroke = document.createElement('div');
// // //Give Style
clauseDiv.classList.add('clauseDivReverse');
clauseText.classList.add('clauseReverse');
clauseText.setAttribute("id", "clauseReverse");
clauseStroke.classList.add('strokeClauseReverse');
//
// // Append to document
clauseDiv.appendChild(clauseText);
clauseDiv.appendChild(clauseStroke);
bottomDivs.appendChild(clauseDiv);
document.body.appendChild(bottomDivs);
})
/***********/
//Create a addDetail
function createDetail(target){
var mainColumn = target.parentElement;
var strokeColumn = mainColumn.children[1];
// Create Elements
var levelOneDiv = document.createElement('div');
var levelOneText = document.createElement('input');
if ( mainColumn.classList.contains('clauseDiv') ) {
levelOneDiv.classList.add('levelOneClauseReverse');
levelOneText.classList.add('levelOneTextReverse');
//I thought you have not completed your style yet, like something levelOneClause class
} else {
levelOneDiv.classList.add('levelOneClauseReverse');
levelOneText.classList.add('levelOneTextReverse');
}
levelOneDiv.appendChild(levelOneText);
strokeColumn.appendChild(levelOneDiv);
}
#import url('https://fonts.googleapis.com/css?family=Vollkorn+SC');
body{
margin: 10%;
margin-right: 15%;
margin-left: 10%;
margin-top: 5%;
}
h1{
color: #3B3C3D;
font-family: 'Vollkorn SC', serif;
font-size: 2em;
text-align:center;
}
h2{
color: #3B3C3D;
font-family: 'Vollkorn SC', serif;
font-size: 1.5em;
text-align:center;
}
#bottomClauses{
clear: both;
float: right;
}
/*CONTROL-PANEL*/
#controlPanel{
float: inline-block;
margin-top: 5%;
margin-left: 20%;
margin-right: 20%;
margin-bottom: 15%;
padding-bottom: 2%;
border-radius: 10%;
border-bottom: 0.1vw solid #3B3C3D;
}
.addClause{
background-color: #2c3e50;
margin-top: 5%;
font-size: 0.75em;
padding: 0.5em;
border: 0;
color: #FFF;
}
.addClause:hover{
cursor: pointer;
background-color: #000;
}
.addDetail{
display: none;
background-color: #2c3e50;
margin-top: 5%;
font-size: 0.75em;
padding: 0.5em;
border: 0;
color: #FFF;
}
.addDetail:hover{
cursor: pointer;
background-color: #000;
}
/*FISHBONE*/
#fishBone{
position: relative;
float:right;
top: 19.75vw;
width: 100%;
height: 0.2vw;
background-color: #34495e;
}
#finalResult{
position: absolute;
/*float:right;*/
left: 83.5vw;
top: 44.25vw;
width: 7.5vw;
height: 7.5vw;
padding: 1vw;
text-align: center;
color: #FFF;
background-color: #7f8c8d;
border-radius: 50%;
border: 0.15vw solid #34495e;
}
/*NEW CLAUSE*/
.clauseDiv{
display: inline-block;
float:right;
width: 5vw;
margin-right: 12.5vw;
}
.clause{
float: inline-block;
position: relative;
top: -3.5vw;
right: 2vw;
text-align: center;
width: 5.8vw;
height: 1.5vw;
padding: 0.2vw;
color: #FFF;
background-color: #3498db;
border-radius: 0.15vw;
border: 0;
}
.strokeClause{
position: relative;
top: -5.75vw;
transform: rotate(-25deg);
background-color: #34495e;
width: 0.1vw;
height: 25vw;
margin-left: 7.5vw;
border: 0.05vw solid #34495e;
border-radius: 0.1vw;
float: inline-block;
z-index: -1;
}
/*NEW CLAUSE REVERSE*/
.clauseDivReverse{
float: inline-block;
float:right;
width: 5vw;
margin-right: 12.5vw;
}
.clauseReverse{
position: relative;
top: 15.5vw;
right: 2.5vw;
float: inline-block;
text-align: center;
width: 5.8vw;
height: 1.5vw;
padding: 0.2vw;
color: #FFF;
background-color: #3498db;
border-radius: 0.15vw;
border: 0;
}
.strokeClauseReverse{
position: relative;
top: -9.75vw;
transform: rotate(25deg);
background-color: #34495e;
width: 0.1vw;
height: 25vw;
margin-left: 7.5vw;
border: 0.05vw solid #34495e;
border-radius: 0.1vw;
float: inline-block;
z-index: -1;
}
/*NEW LEVEL ONE*/
.levelOneClauseReverse{
margin-bottom: 5vw;
}
.levelOneTextReverse{
border: 0;
position: relative;
font-size: 0.75vw;
width: 13vw;
top: 4.5vw;
right: 12.75vw;
border-bottom: 0.1vw solid #34495e;
transform: rotate(-25deg);
}
<body>
<h1>Diagram Editor</h1>
<div id='controlPanel'>
<h2>Control Panel</h2>
<input type='submit' name='addClause' value='Clause on TOP' class='addClause' id='addClauseTop'>
<input type='submit' name='addClause' value='Clause on BOTTOM' class='addClause' id='addClauseBottom'>
<input type='submit' name='addClause' value='Add Detail' class='addDetail' id='addDetail'>
</div>
<div id='fishBone'></div>
<input type='text' name='clause' id='finalResult'>
<div id='topClauses'>
<div class='clauseDiv'>
<input class='clause' id='clause'>
<div class='strokeClause'>
</div>
</div>
</div>
<div id='bottomClauses'>
<div class='clauseDivReverse' >
<input class='clauseReverse clause'>
<div class='strokeClauseReverse'>
<div class='levelOneClauseReverse'>
<input class='levelOneTextReverse'>
</div>
<div class='levelOneClauseReverse'>
<input class='levelOneTextReverse'>
</div>
<div class='levelOneClauseReverse'>
<input class='levelOneTextReverse'>
</div>
<div class='levelOneClauseReverse'>
<input class='levelOneTextReverse'>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
I have a page that I need to convert to rtl.
The active tab & tab arrow position doesn't match.
I need to change the tab arrow position from TAB 1 to TAB 2 and vice versa.
I already changed some js value but in vain
HTML :
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="main-search-container">
<form class="main-search-form">
<div class="search-type">
<label class="active"><input class="first-tab" name="tab" checked="checked" type="radio">TEXT</label>
<label><input name="tab" type="radio">TEXT</label>
<label><input name="tab" type="radio">TEXT</label>
<div class="search-type-arrow"></div>
</div>
<div class="main-search-box">
<div class="main-search-input larger-input">
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
CSS :
body {
direction:rtl;
color: #707070;
background-color: #cccccc;
}
.parallax {
background-repeat: no-repeat;
background-position: 50% 50%;
position: relative;
z-index: 99;
}
.parallax-overlay {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 101;
background-color: #333;
opacity: 0.4;
}
.parallax-content {
position: relative;
z-index: 999;
padding: 105px 0;
}
.main-search-container {
transform: translate3d(0,-12px,0);
}
.main-search-form {
width: 660px;
display: block;
margin: 0 auto;
position: relative;
margin-top: 35px;
}
.search-type {
display: inline-block;
padding-bottom: 35px;
position: relative;
}
.search-type input[type="radio"] { display: none; }
.search-type label {
background-color: #fff;
color: #333;
cursor: pointer;
display:inline-block;
text-align: center;
padding: 9px 18px;
margin: 0 0 0 15px;
float: right;
transition: all 0.2s;
border-radius: 3px;
}
.search-type label:hover,
.search-type label.active {
background-color: #66676b;
color: #fff;
}
.search-type-arrow {
width: 0;
height: 0;
border-right: 15px solid transparent;
border-left: 15px solid transparent;
border-bottom: 15px solid #fff;
position: absolute;
bottom: 0;
right: 0;
transform: translate3d(-3px,0,0);
}
.main-search-box {
background-color: #fff;
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.12);
padding: 30px;
padding-bottom: 20px;
margin-top: -9px;
border-radius: 3px;
}
.main-search-box.no-shadow {
box-shadow: none;
padding: 0;
margin: 0;
}
.search-container .main-search-input input {
font-weight: 500;
font-size: 17px;
height: 57px !important;
float: right;
box-sizing: border-box;
border: none;
float: right;
height: auto;
}
.search-container .main-search-input button.button {
width: initial;
min-width: 100px;
max-width: 100px;
margin: 0;
font-size: 18px;
position: relative;
margin-right: 20px;
flex: 0 auto;
height: 57px;
}
.search-container .main-search-input button.button i {
position: relative;
right: 2px;
}
JS :
(function($){
"use strict";
$(document).ready(function(){
function searchTypeButtons() {
// Radio attr reset
$('.search-type label.active input[type="radio"]').prop('checked',true);
// Positioning indicator arrow
var buttonWidth = $('.search-type label.active').width();
var arrowDist = $('.search-type label.active').position().left;
$('.search-type-arrow').css('right', arrowDist + (buttonWidth/2) );
$('.search-type label').on('change', function() {
$('.search-type input[type="radio"]').parent('label').removeClass('active');
$('.search-type input[type="radio"]:checked').parent('label').addClass('active');
// Positioning indicator arrow
var buttonWidth = $('.search-type label.active').width();
var arrowDist = $('.search-type label.active').position().left;
$('.search-type-arrow').css({
'right': arrowDist + (buttonWidth/2),
'transition':'right 0.4s cubic-bezier(.87,-.41,.19,1.44)'
});
});
}
// Init
if ($(".main-search-form").length){
searchTypeButtons();
$(window).on('load resize', function() { searchTypeButtons(); });
}
// ------------------ End Document ------------------ //
});
})(this.jQuery);
I'm using bootstrap, bootstrap rtl flipped & jquery 2.2.0 as external ressources.
Here's the snippet:
https://jsfiddle.net/s3hy37nd/5/
Can someone help with that?
So many errors... I can only say I tried to comment them all inside the code so... yeah it's all there:
"use strict";
jQuery(function($) { // DOM ready and $ alias secured
// Radio attr reset (on DOM ready... makes sense?)
$('.search-type label.active input[type="radio"]').prop('checked', true);
function repositionArrow() { // Use a meaningful fn name
// Positioning indicator arrow
// Width? No. You need .outerWidth! you have paddings!!
var buttonWidth = $('.search-type label.active').outerWidth();
// Again use meaningful names
// If you do console.log( $('.search-type label.active').position() );
// you'll see no `.right` property. So yeah, use .left
var posLeft = $('.search-type label.active').position().left;
$('.search-type-arrow').css({
left: posLeft + (buttonWidth / 2)
// No need for transition here - move it to Stylesheet instead
});
}
// Init
if ($(".main-search-form").length) {
// You might want this too inside the "if"
$('.search-type label').on('change', function() {
$('.search-type input[type="radio"]').parent('label').removeClass('active');
$('.search-type input[type="radio"]:checked').parent('label').addClass('active');
repositionArrow(); // Now you have such function
});
repositionArrow();
$(window).on('load resize', repositionArrow);
}
});
body {
direction: rtl;
color: #707070;
background-color: #cccccc;
}
.parallax {
background-repeat: no-repeat;
background-position: 50% 50%;
position: relative;
z-index: 99;
}
.parallax-overlay {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 101;
background-color: #333;
opacity: 0.4;
}
.parallax-content {
position: relative;
z-index: 999;
padding: 105px 0;
}
.main-search-container {
transform: translate3d(0, -12px, 0);
}
.main-search-form {
width: 660px;
display: block;
margin: 0 auto;
position: relative;
margin-top: 35px;
}
.search-type {
display: inline-block;
padding-bottom: 35px;
position: relative;
}
.search-type input[type="radio"] {
display: none;
}
.search-type label {
position: relative;
/* YOU NEED SOME POSITION! */
background-color: #fff;
color: #333;
cursor: pointer;
display: inline-block;
text-align: center;
padding: 9px 18px;
margin: 0 0 0 15px;
/*float: right; WHY? you use rtl already */
transition: all 0.2s;
border-radius: 3px;
}
.search-type label:hover,
.search-type label.active {
background-color: #66676b;
color: #fff;
}
.search-type-arrow {
width: 0;
height: 0;
border-right: 15px solid transparent;
border-left: 15px solid transparent;
border-bottom: 15px solid #fff;
position: absolute;
bottom: 0;
/*right: 0; ...Nope. See JS, we need left */
left: calc(100% - 30px);
/* calc to the rescue */
/*transform: translate3d(-3px,0,0); but why */
transition: left 0.4s cubic-bezier(.87, -.41, .19, 1.44);
/* Moved from JS */
}
.main-search-box {
background-color: #fff;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.12);
padding: 30px;
padding-bottom: 20px;
margin-top: -9px;
border-radius: 3px;
}
<div class="parallax-content">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="main-search-container">
<form class="main-search-form">
<div class="search-type">
<label class="active"><input class="first-tab" name="tab" checked="checked" type="radio">TAB 1</label>
<label><input name="tab" type="radio">TAB 2</label>
<label><input name="tab" type="radio">TAB 3</label>
<div class="search-type-arrow"></div>
</div>
<div class="main-search-box">
<div class="main-search-input larger-input">
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
Updated jsFiddle