Foundation Range Slider with 2 Inputs? - slider

Is it possible to have 2 different inputs controlling a Foundation range slider? I need to add a field to set specific fractions (like so): https://codepen.io/jinch/pen/OaKpZX?editors=1000
<div class="row">
<div class="small-2 columns">Width:</div>
<div class="small-5 columns">
<div class="slider" data-slider data-initial-start="20" data-options="precision:3; decimal:3; start:8; end:36;" style="margin-top: 12px;">
<span class="slider-handle slideWidth" data-slider-handle role="slider" tabindex="1" aria-controls="customPort-2-width"></span>
<span class="slider-fill" data-slider-fill></span>
</div>
</div>
<div class="small-5 columns">
<input type="number" step="1" id="customPort-2-width" style="border: 1px solid #999999; display:inline-block; width:39%;">
<select class="fractions fractionWidth" name="WidthFraction" style="border: 1px solid #999999; display:inline-block; width:39%;">
<option value="0" selected="">0</option>
<option value="1/8">1/8</option>
<option value="1/4">1/4</option>
<option value="3/8">3/8</option>
<option value="1/2">1/2</option>
<option value="5/8">5/8</option>
<option value="3/4">3/4</option>
<option value="7/8">7/8</option>
</select>
<span class="small">in.</span>
</div>

TL;DR The good news is this can be done! The bad news is that you'll need to adjust your code a bit. I'm typing this out at ten minutes past midnight so you will more than likely need to refactor this 😂but I hope it's a starting point.
I'll go into the reasons for the implementation, but if you want to just get into it, I've created a Codepen https://codepen.io/jamie-endeavour/pen/zyxPaY?editors=1010
A Bit of Background
When I first found this question I looked at the Slider documentation and noticed a section about 'Reflow' - https://foundation.zurb.com/sites/docs/slider.html#reflow - after reading about this method I decided to look in the Foundation source code:
_reflow() {
this.setHandles();
}
Then, I looked into setHandles()...
setHandles() {
if(this.handles[1]) {
this._setHandlePos(this.$handle, this.inputs.eq(0).val(), true, () => {
this._setHandlePos(this.$handle2, this.inputs.eq(1).val(), true);
});
} else {
this._setHandlePos(this.$handle, this.inputs.eq(0).val(), true);
}
}
Notice here that _setHandlePos() makes a reference to a value from a property called inputs.
When the Slider class is initialized the following are set:
this.inputs = this.$element.find('input');
this.$input = this.inputs.length ? this.inputs.eq(0) : $(`#${this.$handle.attr('aria-controls')}`);
In your example, you weren't supplying an input element. So as you can see the code is using the value of aria-controls to build up the selector of the 'input'. This is where your restriction lies as you won't be able to associate two input fields with the Slider this way.
The Solution
So, remember the ternary that was used in this.$input? If we add an <input> within your .slider element, then we can use this as a reference point for the Slider.
<div class="slider" data-slider data-initial-start="20" data-options="precision:3; decimal:3; start:8; end:36;" style="margin-top: 12px;">
<input type="hidden" id="slider-reference" value="8" />
...
Now that you have a reference that can be used between the two input fields. You can then update this reference field's value on the back of a change callback against each element, then Slider will read this and update accordingly.
The JS required for the number input is fairly straightforward as we are supplying the absolute number that Slider needs to calculate the width position.
The fraction input gets a bit funky because you need to return a fraction of the difference between your min and max value (28) rather than a fraction of your max value (36 in this case). This is why I've included additional calculations to ensure you get the correct position against the Slider element. This is also the part that I feel can be refactored with a fresh set of eyes 😬
Finally, once you have acquired the new values in each change handler you simply call the _reflow method and this will update the Slider plugin accordingly without you having to re-initialize the plugin or anything nasty like that!

Related

How to line break the selected option text in a MaterializeCSS select

I have a MaterializeCSS form with selects which have more text than the default width can show.
Currently, the text is a single line that is simply cut off at the widget edge.
The expanded dropdown of options line breaks the text correctly, but the collapsed dropdown does not.
I want to resize the collapsed select dropdown so that all text is shown.
My form is generated by django-material in a Django app, but I believe my fundamental problem is with MaterializeCSS's select widget.
An example element looks like this:
<div class="select-field col s12 required" id="id_formset-organisation_answers-1-question_container">
<label for="id_formset-organisation_answers-1-question">Question</label>
<div class="select-wrapper">
<input class="select-dropdown dropdown-trigger" type="text" readonly="true"
data-target="select-options-f739ead4-0165-a758-650e-74ba61fac041">
<ul id="select-options-f739ead4-0165-a758-650e-74ba61fac041"
class="dropdown-content select-dropdown"
tabindex="0">
<li id="select-options-f739ead4-0165-a758-650e-74ba61fac0410" tabindex="0">
<span>---------</span>
</li>
<li id="select-options-f739ead4-0165-a758-650e-74ba61fac0411" tabindex="0">
<span>VERY LONG TEXT 1</span>
</li>
<li id="select-options-f739ead4-0165-a758-650e-74ba61fac0412" tabindex="0"
class="selected">
<span>VERY LONG TEXT 2</span>
</li>
<li id="select-options-f739ead4-0165-a758-650e-74ba61fac0413" tabindex="0">
<span>VERY LONG TEXT 3</span>
</li>
</ul>
<svg class="caret" height="24" viewBox="0 0 24 24" width="24"
xmlns="http://www.w3.org/2000/svg">
<path d="M7 10l5 5 5-5z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
<select id="id_formset-organisation_answers-1-question"
name="formset-organisation_answers-1-question" tabindex="-1">
<option value="">---------</option>
<option value="1">VERY LONG TEXT 1</option>
<option value="2" selected="selected">VERY LONG TEXT 2</option>
<option value="3">VERY LONG TEXT 3</option>
</select>
</div>
<div class="help-block">The question.</div>
</div>
The element <input class="select-dropdown dropdown-trigger" type="text" readonly="true" data-target="..."> does not contain any inner HTML, but by some JS magig shows the selected option. That option, a very long text, is shown as a single line. That line is what I want to line break.
I have searched the MaterializeCSS docs, SO, and the web to figure out a way to force the text to linebreak. The obvious answer seems to be to apply CSS styles for display, overflow and the like.
Style via CSS
I can access the selected text from CSS:
input.select-dropdown {
/* This works: */
color:green;
min-height: 20rem;
/* This does not do anything: */
display: inline-block;
min-height: -moz-fit-content;
min-height: fit-content;
overflow-wrap: break-word;
z-index:10;
}
The above CSS makes the text in each select green and increases the box height, but still comes as a single line with invisible overflow.
I have experimented with different CSS selectors to make sure that we're targeting the correct element here.
No permutation of any CSS class here could get that line to wrap.
Style via JS
MaterializeCSS initialises the widgets through a lot of JS events on page load.
I can also override the styles using JS after page load by inserting:
/**
* Resize select dropdowns to fit text
* #see https://materializecss.com/select.html
*/
window.addEventListener('load', function() {
document.querySelectorAll('select').forEach((el) => {
console.log("Styling element " + el.id);
el.M_FormSelect.input.style.cssText="color:red;";
console.log(el.M_FormSelect.input.style);
});
So I can now take the text that was green as per my CSS and make it red. Again, no style related to text flow (overflow, display, etc) is able to break the text here either.

text() returning entire object, instead of selected text

I upgraded the jquery library from 1.4.2 to 3.4.0 Have a drop down. .val() is returning the selected value as expected, but .text() is returning is entire object. Need to know how to get the displayed text for the selected option.
$('#properties').change(function(){
var selected_id=$('#properties option:selected').val();
var selected_value=$('#properties option:selected').text();
$('#idresult').text(selected_id);
$('#textresult').text(selected_value);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<select id="properties" style="width: 100%; height: 80px;" size="6">
<option value="1">Country</option>
<option value="3">City</option>
<option value="4">Metro Area (Only U.S.)</option>
<option value="2">State/Province</option>
<option value="5">Zip Code (Only U.S.)</option>
</select>
<div id="textresult"></div>
<div id="idresult"</div>
it is working fine when I tried it on jsfiddle and stackoverflow code snippet. But in my code, it is not giving the below values. Any ideas what might have gone wrong. It works fine when I switch back jquery to 1.4.2
selected_id is 1 as expected.
selected_value is k.fn.init [option, prevObject: k.fn.init(1)].
Expecting it to be "Country"

BEM. How to deal with label for/id?

As far as I know, BEM does not use elements id at all. But how to deal in this case with the label for/id combined with inputs? If I do not use id, people who're using screen readers will not get that this label is for that input. Am I right?
BEM suggests avoiding id for css selectors. It's perfectly valid to use id for A11y and usability.
<form class="form" method="post" action="">
<label for="email" class="form__label">Email</label>
<input type="email" id="email" class="form__control"/>
</form>
In the example above we are styling the input as an Element of the form block with the form__control class.
Also you can not use aria attributes without id for pointers to descriptive elements.
<div role="application" aria-labelledby="calendar" aria-describedby="info">
<h1 id="calendar">Calendar</h1>
<p id="info">
This calendar shows the game schedule for the Boston Red Sox.
</p>
<div role="grid">
...
</div>
</div>
Example taken from: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-describedby_attribute

options for search TextField in navbar

Is it possible that the user could select from the utility dropdown (see picture below) a search option which will effect the search text field?
Or is it possible merge the dropdown and search text field together?
I think you will be able to do this leveraging input-groups. But the docs also mention:
Avoid using elements here as they cannot be fully styled in
WebKit browsers.
HTML to integrate a <select> in the navbar:
<form class="navbar-form navbar-left select-search" role="search">
<div class="input-group">
<span class="input-group-addon">
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</span>
<input type="text" class="form-control">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
Then apply the following CSS:
.select-search .input-group-addon {
padding: 0;
border: 0;
}
.select-search .input-group .input-group-addon > select.form-control {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
Demo: http://www.bootply.com/8zPGZ9ySOl
I found this works well in chrome, but in firefox the select arrow got some button like styling:
I'm not sure how to fix the style of the select arrow here. Style the select with -moz-appearance:none or -moz-appearance:menulist won't help. Possible related to: Clearing the background of a <select> element's pulldown button with CSS

What is the Twitter Bootstrap convention for adding a new style to form labels?

I want to create a new style for forms in my Twitter Bootstrap site and I want to keep with the SMACKS / OOCSS conventions.
This is the default form:
<form role="form">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
</div>
<!-- More coontent in form here -->
If I want to override the input style it would seem to me to keep with conventions to add a class of form-control-newstyle:
<form role="form">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
</div>
<!-- More coontent in form here -->
However I also want to style the label. Should I add a new style to the label or to the div.form-group?
.form-group wraps around form groups label and an input, it acts like a .row in a .form-horizontal and otherwise, when the form stacks on smaller viewports there's some vertical space so it all doesn't squish together.
The default label style that goes on all labels is:
label {
display: inline-block;
max-width: 100%;
margin-bottom: 5px;
font-weight: bold;
}
To do
.form-group label {}
Would affect all labels if you've properly formatted your forms.
If you just want to isolate it to a specific label you can make a class for that label and assign a class to just that label. If you want to affect all labels you can just modify the label element.
You can also make a new parent class for anything wrapping around that form and address the label styling like that.