I'm not a JS expert but i think what i'm trying to do is pretty simple (at least in jQuery)
I've got 3 select
<select id="faq" class="onchance_fill">...</select>
<select id="pages" class="onchance_fill">...</select>
<select id="faq" class="onchance_fill">...</select>
and an input (it's a tinyMCE one in advlink plugin)
<input type="text" onchange="selectByValue(this.form,'linklisthref',this.value);" value="" class="mceFocus" name="href" id="href" style="width: 260px;">
I want that each time i change a value in one of the 3 select, that this value of the option, will be placed in the input.
In Jquery, it would be something like :
$('.ajax_onchance_fill').change(function() {
data = $('.ajax_onchance_fill').val();
$('#href').text(data);
});
But i can't use it. So what is the equivalent in plain Javascript ?
Thanks
I would advice you keep using Jquery as it speeds up this kind of thing but in pure JavaScript i think what you want looks something like this...
<script type="text/javascript">
function load() {
var elements = document.getElementsByClassName('onchance_fill');
for(e in elements){
elements[e].onchange = function(){
document.getElementById('href').value = this.value;
}
}
}
</script>
document.getElementsByClassName("ajax_onchance_fill").onchange = function() {
getElementById('href').value = this.options[this.selectedIndex].text;
};
Though I am not sure exactly if it'll work since getElementsByClassName returns more than 1 element.
Try this:
$('.ajax_onchance_fill').change(function() {
var data = $(this).val();
$('#mytextboxid').val(data);
});
Okay, so I realize this thread is well over 8 years old, so this answer isn't so much for the OP (who probably figured it out long ago) as it is for someone else who might be curious about this particular topic.
All that said, here's a relatively simple and reliable way you could pull it off in vanilla JS:
/**
* Since we need to listen to all three ajax_onchance_fill elements,
* we'll use event delegation.
*
*/
const targetLink = document.getElementById('href');
document.addEventListener('change', function(e) {
if (!!Element.prototype.matches) { // Let's make sure that matches method is supported...
if (e.target.matches('.ajax_onchance_fill')) {
targetLink.textContent = e.target.value;
}
} else { // and if not, we'll just use classList.contains...
if (e.target.classList.contains('ajax_onchance_fill')) {
targetLink.textContent = e.target.value;
}
}
});
Related
Currently, I am quite new to javascript and I came across one of the javascript keyword (.logic). I tried to googled this keyword, but I couldn't find the answer to it. Can anyone please explain what is the meaning of it? When, why should we use it? It seem like it is create the object in a logical order or something.
Here is the snippet of the code:
var checkboxes = slice(document.querySelectorAll('.checkbox'));
for (var checkbox of checkboxes)
checkbox.logic = new Checkbox(checkbox);
***Note: I tried to learn ARIA right now. that is why I don't use the native checkbox.*
Here is the code for the HTML part:
<div class="demo">
<h2>Custom checkboxes</h2>
<div tabindex="0" class="checkbox" checked>
Tim-Tams
</div>
<div tabindex="0" class="checkbox">
Mint slices
</div>
</div>
Here is the javascript code:
function Checkbox(el) {
this.el = el;
this.el.addEventListener('keydown', this.handleKeyDown.bind(this));
this.el.addEventListener('click', this.toggle.bind(this));
// Initialize role and aria-checked state.
this.el.setAttribute('role', 'checkbox');
if (this.el.hasAttribute('checked')) {
this.el.setAttribute('aria-checked', 'true');
} else {
this.el.setAttribute('aria-checked', 'false');
}
}
Checkbox.prototype.handleKeyDown = function(e) {
switch(e.keyCode) {
case VK_ENTER:
case VK_SPACE: {
this.toggle();
break;
}
}
};
Checkbox.prototype.toggle = function() {
if (this.el.hasAttribute('checked')) {
this.el.removeAttribute('checked');
// Keep checked attribute and aria-checked in sync.
this.el.setAttribute('aria-checked', 'false');
} else {
this.el.setAttribute('checked', '');
// Keep checked attribute and aria-checked in sync.
this.el.setAttribute('aria-checked', 'true');
}
};
//HERE IS THE QUESTION PART================================
var checkboxes = slice(document.querySelectorAll('.checkbox'));
for (var checkbox of checkboxes)
checkbox.logic = new Checkbox(checkbox);
So, I'm pretty junior at prototypal inheritance, so anyone please chime in to verify/clarify my answer.
From what I understand, .logic is not a native JavaScript function or method, rather it is adding a property to an object as part of prototypal inheritance.
What this snippet of code is doing...
var checkboxes = slice(document.querySelectorAll('.checkbox'));
for (var checkbox of checkboxes)
checkbox.logic = new Checkbox(checkbox);
...is turning the custom html checkboxes into objects objects using the .slice function (these are array-like, so they can turn into objects).
In short, checkbox.logic is similar to....
var checkbox = {logic: "doing stuff with the Checkbox class that was called"};
Not a perfect metaphor but I hope that clarifies this.
I have following problem. Let's say I have DOM like this.
<div class="results">
<div class="result">
<div class="title">Aaa</div>
</div>
<div class="result filtered-out">
<div class="title">Aab</div>
</div>
<div class="result">
<div class="title">Aac</div>
</div>
<div class="result">
<div class="title">Aad</div>
</div>
<div class="result">
<div class="title">Aae</div>
</div>
</div>
and an input field like this
<input type="text" id="search">
And now I try to filter the results with a simple function defined by this
var searchBox = $(this);
searchBox.keyup(function(){
var searchBox = $(this);
var items = $(".results .result:not(.filtered-out)");
items.each(function(){
var title = $(this).find(".title").html();
if(title.toLowerCase().indexOf(searchBox.val().toLowerCase())!== -1)
$(this).show();
else
$(this).hide();
});
});
So the problem is that the list of results is quite long something between 100 and 200 elements and whenever I type something into the search input the code executes very long. Maybe around 2-3 seconds. Is there any other approach to solve this "lag"? Thank you for any advices!
EDIT Maybe something like delayed script execution or asynchronous script execution (like in ajax)?
It's generally not a good idea to use the DOM as a datasource, it's not meant for it and is therefore slow. Personally I would recommend using a small MVVM library or something similar so you don't have to manually manage the DOM yourself.
I've used Vue.js below, but you could just as well use any similar solution. Keeping your data in the code will allow you to operate on it a lot faster since you don't have to re-request it all the time and you avoid doing a lot of work for modifications. All operations below are done on 1000 objects:
var items = [];
for (var i = 0; i < 1000; i++) {
items.push({
title: 'Item #' + i
});
}
var v = new Vue({
el: '#list',
data: {
items: items,
input: ""
},
computed: {
filteredItems: function() {
var value = ("" || this.input).trim().toLowerCase();
if (!value.length) return this.items;
return this.items.filter(function(item) {
return item.title.toLowerCase().indexOf(value) !== -1;
});
}
}
});
ol {
list-style: none;
padding: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.12.16/vue.min.js"></script>
<div id="list">
<input placeholder="Search" v-model="input" />
<ol>
<li v-repeat="filteredItems">{{title}}</li>
</ol>
</div>
When searching the dom through many elements it is recommended to use javascript as opposed to jQuery if speed is what you are after. jQuery has it's purpose but for large amounts of dom searching using javascripts getElementById or querySelector / querySelectorAll is going to be much much faster. If you check this jsPerf example you can see that the jQuery selector operates roughly 94% slower than the comparable getElementById.
You should try using some logging to figure out which part is taking the longest. If you find that it's the items selector (with the psuedo-not), you could try to optimize that, however I don't see anything about the filtered-out class so I'm not sure exactly what that does.
Here's some simple optimizations though:
var searchBox = $(this);
searchBox.keyup(function(){
var $searchBox = $(this);
var searchBoxVal = $searchBox.val().toLowerCase();
var items = $(".results .result:not(.filtered-out)");
items.each(function(){
var $item = $(this);
var title = $item.find(".title").html();
if (title.toLowerCase().indexOf(searchBoxVal) !== -1)
$item.show();
else
$item.hide();
});
});
My guess the lag is because you are performing the search based on the DOM elements, and at the same time manipulating them with hiding/ showing.
I suppose the DOM is populated from some data source? If so it'll be better to perform the search/ filter from that data source, then use the filtered data set to populate the DOM again. (And even if you don't have the data source at first, you can build one by reading the original DOM)
Pretty sure I know the solution... would write .on('change','load', function(){}
correct? <-- Tested didn't work? so I am up to your solutions :)
Sushanth -- && adeneo both came up with great solutions, this is a good lesson in optimizing code... It's gonna be hard to choose which answer to go with, but I know this is going to help me rethink how I write... I dont know what I do without this forum, id have to learn this stuff in college.
This is purely a question out of curiosity and bettering my skills, as well as giving you guys a chance to display your knowledge on jQuery. Also to prevent any sloppy writing.
I have a radio based switch box, the markup looks like this, the id's and on/off values are generated by the values in my array with PHP...
<span class="toggle-bg">//This color is the background of the toggle, I need jQuery to change this color based on the state on/off
<input type="radio" value="on" id="_moon_page_header_area1" name="_moon_page_header_area">//this is my on value generated by the array
<input type="hidden" value="_moon_page_header_area" class="switch-id-value">// I create this input because I have multiple checkboxes that have the ID _moon_ARRAYVALUE_area1
<input type="radio" value="off" id="_moon_page_header_area2" name="_moon_page_header_area">// off value
<input type="hidden" value="_moon_page_header_area" class="switch-id-value">//_moon_ARRAYVALUE_area2
<span class="switch"></span>// the switch button that changes
</span>
Hope that makes sense and the comments are clear
Here is the jQuery
var value = $('.toggle-bg input.switch-id-value').val()
var moon1 = $('#'+value+'1').is(':checked');
var moon2 = $('#'+value+'2').is(':checked');
var static_slide = $('._moon_staticarea_height');
var toggle = $('.toggle-bg');
if(moon1){
toggle.css({'background-color': '#46b692'});
static_slide.hide()
} else
if (moon2){
toggle.css({'background-color': '#333'});
static_slide.show()
}
$('.toggle-bg').change(function () {
var value = $('.toggle-bg input.switch-id-value').val()
var moon1 = $('#'+value+'1').is(':checked');
var moon2 = $('#'+value+'2').is(':checked');
var static_slide = $('._moon_staticarea_height');
var toggle = $('.toggle-bg');
if(moon1){
toggle.css({'background-color': '#46b692'});
static_slide.slideUp()
} else
if (moon2){
toggle.css({'background-color': '#333'});
static_slide.slideDown()
}
});
it looks longer than it really is, its just repeating it self, one is on load so that it gives the correct color on load of the page, and then inside the change function we need to change colors..
How do I write it so I only have to use variables one time (so its cleaner) is there a better way to optimize it... Just NOW thinking after writing this I could put it in one function .on('load', 'change', function() {}
I just now thought of that, but I wrote all this so I am going to see what others think...
You'd do that by having the function in the change event handler, and on the end you chain on a trigger('change') to make it work on pageload :
$('.toggle-bg').on('change', function () {
var value = $('.toggle-bg input.switch-id-value').val(),
moon1 = $('#' + value + '1').is(':checked'),
slider = $('._moon_staticarea_height'),
toggle = $('.toggle-bg');
toggle.css('background-color', (moon1 ? '#46b692' : '#333'));
slider[moon1?'slideUp':'slideDown']();
}).trigger('change');
As radiobuttons can't be unchecked, it's either moon1 or moon2, which means checking one of them should be enough.
.on('change','load',
supposed to be
// Remove the comma separator if you want to bind the same handler to
// multiple events.
.on('change load',
And you can remove the one separately written out and enclose it in a function (if multiple instances of the class toggle-bg)
or just trigger the change event.(If there is a single instance of a class)
This will just run the same functionality when the page loads.
var toggle = $('.toggle-bg');
toggle.change(function () {
var value = $('input.switch-id-value', this).val(),
moon1 = $('#' + value + '1').is(':checked'),
moon2 = $('#' + value + '2').is(':checked'),
static_slide = $('._moon_staticarea_height');
if (moon1) {
toggle.css({
'background-color': '#46b692'
});
static_slide.slideUp()
} else if (moon2) {
toggle.css({
'background-color': '#333'
});
static_slide.slideDown()
}
}).change();
I thought this would be a simple hack, but I've now been searching for hours and can't seen to find the right search term. I want to have an ordinary multiple select box (<select multiple="multiple">) except I don't want the user to have to hold down the control key to make multiple selections.
In other words, I want a left click to toggle the <option> element that's under the cursor without changing any of the others. In other other words, I want something that looks like a combo list box but behaves like a group of check boxes.
Can anybody suggest a simple way to do this in Javascript? Thanks.
Check this fiddle: http://jsfiddle.net/xQqbR/1022/
You basically need to override the mousedown event for each <option> and toggle the selected property there.
$('option').mousedown(function(e) {
e.preventDefault();
$(this).prop('selected', !$(this).prop('selected'));
return false;
});
For simplicity, I've given 'option' as the selector above. You can fine tune it to match <option>s under specific <select> element(s). For ex: $('#mymultiselect option')
Had to solve this problem myself and noticed the bugged behavior a simple interception of the mousedown and setting the attribute would have, so made a override of the select element and it works good.
jsFiddle: http://jsfiddle.net/51p7ocLw/
Note: This code does fix buggy behavior by replacing the select element in the DOM. This is a bit agressive and will break event handlers you might have attached to the element.
window.onmousedown = function (e) {
var el = e.target;
if (el.tagName.toLowerCase() == 'option' && el.parentNode.hasAttribute('multiple')) {
e.preventDefault();
// toggle selection
if (el.hasAttribute('selected')) el.removeAttribute('selected');
else el.setAttribute('selected', '');
// hack to correct buggy behavior
var select = el.parentNode.cloneNode(true);
el.parentNode.parentNode.replaceChild(select, el.parentNode);
}
}
<h4>From</h4>
<div>
<select name="sites-list" size="7" multiple>
<option value="site-1">SITE</option>
<option value="site-2" selected>SITE</option>
<option value="site-3">SITE</option>
<option value="site-4">SITE</option>
<option value="site-5">SITE</option>
<option value="site-6" selected>SITE</option>
<option value="site-7">SITE</option>
<option value="site-8">SITE</option>
<option value="site-9">SITE</option>
</select>
</div>
techfoobar's answer is buggy, it unselects all options if you drag the mouse.
Sergio's answer is interesting, but cloning and removing events-bound to a dropdown is not a nice thing.
Try this answer.
Note: Doesn't work on Firefox, but works perfectly on Safari/Chrome/Opera. (I didn't test it on IE)
EDIT (2020)
After 5 years since my original answer, I think best practice here is to replace the dropdown with checkboxes. Think about it, that's the main reason why checkboxes exist in the first place, and it works nicely with old browsers like IE & modern mobiles without any custom JS to handle all the wacky scenarios.
Necromancing.
The selected answer without jQuery.
Also, it missed setting the focus when an option is clicked, because you have to do this yourself, if you write e.preventDefault...
Forgetting to do focus would affect CSS-styling, e.g. bootstrap, etc.
var options = [].slice.call(document.querySelectorAll("option"));
options.forEach(function (element)
{
// console.log("element", element);
element.addEventListener("mousedown",
function (e)
{
e.preventDefault();
element.parentElement.focus();
this.selected = !this.selected;
return false;
}
, false
);
});
I had same problem today, generally the advice is to use a list of hidden checkboxes and emulate the behavior via css, in this way is more easy to manage but in my case i don't want to modify html.
At the moment i've tested this code only with google chrome, i don't know if works with other browser but it should:
var changed;
$('select[multiple="multiple"]').change(function(e) {
var select = $(this);
var list = select.data('prevstate');
var val = select.val();
if (list == null) {
list = val;
} else if (val.length == 1) {
val = val.pop();
var pos = list.indexOf(val);
if (pos == -1)
list.push(val);
else
list.splice(pos, 1);
} else {
list = val;
}
select.val(list);
select.data('prevstate', list);
changed = true;
}).find('option').click(function() {
if (!changed){
$(this).parent().change();
}
changed = false;
});
Of course suggestions are welcome but I have not found another way
Reusable and Pure JavaScript Solution
const multiSelectWithoutCtrl = ( elemSelector ) => {
let options = document.querySelectorAll(`${elemSelector} option`);
options.forEach(function (element) {
element.addEventListener("mousedown",
function (e) {
e.preventDefault();
element.parentElement.focus();
this.selected = !this.selected;
return false;
}, false );
});
}
multiSelectWithoutCtrl('#mySelectInput') /* Can use ID or Class */
option {
font-size: 20px;
padding: 10px 20px;
}
<select multiple id="mySelectInput" class="form-control">
<option>🍎 Apple</option>
<option>🍌 Banana</option>
<option>🍍 Pineapple</option>
<option>🍉 Watermelon</option>
</select>
I've been writing JS (mainly jQuery) for quite a few months now, but today I decided to make my first abstraction as a jQuery method. I already have working code but I feel/know that I'm not doing it the right way, so I come here for some enlightenment.
Note: Please do not reply that there's already something out there that does the trick as I already know that. My interest in this matter is rather educational.
What my code is intended to do (and does):
Limit the characters of a textfield and change the color of the counter when the user is approaching the end.
And here's what I have:
$(function(){
$('#bio textarea').keyup(function(){
$(this).char_length_validation({
maxlength: 500,
warning: 50,
validationSelector: '#bio .note'
})
})
$('#bio textarea').trigger('keyup');
})
jQuery.fn.char_length_validation = function(opts){
chars_left = opts.maxlength - this.val().length;
if(chars_left >= 0){
$(opts.validationSelector + ' .value').text(chars_left);
if(chars_left < opts.warning){
$(opts.validationSelector).addClass('invalid');
}
else{
$(opts.validationSelector).removeClass('invalid');
}
}
else{
this.value = this.value.substring(0, opts.maxlength);
}
}
In the HTML:
<div id="bio">
<textarea>Some text</textarea>
<p class="note>
<span class="value">XX</span>
<span> characters left</span>
</p>
</div>
Particularly I feel really uncomfortable binding the event each on each keyup instead of binding once and calling a method later.
Also, (and hence the title) I need to call the method initially (when the page renders) and then every time the user inputs a character.
Thanks in advance for your time :)
chars_left is a global variable which is not good at all. Here is a better (slightly changed) version:
jQuery.fn.char_length_validation = function(opts) {
this.each(function() {
var chars_left = opts.maxlength - $(this).val().length;
$(this).keyup(function() {
chars_left = opts.maxlength - $(this).val().length;
if (chars_left >= 0) {
$(opts.validationSelector).text(chars_left);
if (chars_left < opts.warning) {
$(opts.validationSelector).addClass('invalid');
}
else {
$(opts.validationSelector).removeClass('invalid');
}
}
else {
$(this).val($(this).val().substring(0, opts.maxlength));
}
});
});
this.keyup(); // makes the "initial" execution
return this;
};
See a DEMO.
Some explanation:
In a jQuery plugin in function, this refers to the elements selected by the selector. You should use this.each() to loop over all of these and set up every element accordingly.
In this example, every element gets its on chars_left variable. The event handler passed to keyup() has access to it as it is a closure. Update: It is already very late here ;) It is not necessary to declare it here as you recompute the value every time anyway. Still, it should give you an idea how to have private variables that persist over time.
You should always return this to support chaining.
Further thoughts:
You might want to think about how you could make it work for several textareas (i.e. you have to think about the validation selector). Don't tie it to a specific structure.
You should have default options.
Update: Of course you can make your plugin work with only one textarea (like some jQuery functions work).
You can do the binding and initial triggering in the method:
jQuery.fn.charLengthValidation = function(opts) {
return this.keyup(function() {
var charsLeft = opts.maxLength - $(this).val().length;
if (charsLeft >= 0) {
$(opts.validationSelector + ' .value').text(charsLeft);
$(opts.validationSelector).toggleClass('invalid', charsLeft < opts.warning);
} else {
$(this).val($(this).val().substring(0, opts.maxLength));
}
}).trigger('keyup');
}
$(function() {
$('#bio textarea').charLengthValidation({
maxLength: 25,
warning: 10,
validationSelector: '#bio .note'
});
});