Cloning a JS element that contains a chained select - javascript

I've created a script to clone a div, which works fine, however but the javascript chained function within it no longer works for any cloned elements. Works ok for the first one as expected.
I've created a jsfiddle here: https://jsfiddle.net/bvcebmbw/
The javascript is as follows (for both chained and clone functions)
$(document).ready(function () {
$(".Targeting").each(function () {
$('#TargetingAdd').click(function () {
var num = $('.clonedTargeting').length, // Checks to see how many "duplicatable" input fields we currently have
newNum = new Number(num + 1), // The numeric ID of the new input field being added, increasing by 1 each time
newElem = $('#entryTargeting' + num).clone().attr('id', 'entryTargeting' + newNum).fadeIn('slow'); // create the new element via clone(), and manipulate it's ID using newNum value
// Insert the new element after the last "duplicatable" input field
$('#entryTargeting' + num).after(newElem);
// Enable the "remove" button. This only shows once you have a duplicated section.
$('#TargetingDel').attr('disabled', false);
init(newElem.children(".Targeting"));
// Right now you can only add 4 sections, for a total of 5. Change '5' below to the max number of sections you want to allow.
if (newNum == 5)
$('#TargetingAdd').attr('disabled', true).prop('value', "You've reached the limit"); // value here updates the text in the 'add' button when the limit is reached
});
$('#TargetingDel').click(function () {
// Confirmation dialog box. Works on all desktop browsers and iPhone.
if (confirm("Are you sure you wish to remove this Targeting?")) {
var num = $('.clonedTargeting').length;
// how many "duplicatable" input fields we currently have
$('#entryTargeting' + num).slideUp('slow', function () {
$(this).remove();
// if only one element remains, disable the "remove" button
if (num - 1 === 1)
$('#TargetingDel').attr('disabled', true);
// enable the "add" button
$('#TargetingAdd').attr('disabled', false).prop('value', "add section");
});
}
return false; // Removes the last section you added
});
});
// Enable the "add" button
$('#TargetingAdd').attr('disabled', false);
// Disable the "remove" button
$('#TargetingDel').attr('disabled', true);
});
;(function($, window, document, undefined) {
"use strict";
$.fn.chained = function(parent_selector, options) {
return this.each(function() {
/* Save this to child because this changes when scope changes. */
var child = this;
var backup = $(child).clone();
/* Handles maximum two parents now. */
$(parent_selector).each(function() {
$(this).bind("change", function() {
updateChildren();
});
/* Force IE to see something selected on first page load, */
/* unless something is already selected */
if (!$("option:selected", this).length) {
$("option", this).first().attr("selected", "selected");
}
/* Force updating the children. */
updateChildren();
});
function updateChildren() {
var trigger_change = true;
var currently_selected_value = $("option:selected", child).val();
$(child).html(backup.html());
/* If multiple parents build classname like foo\bar. */
var selected = "";
$(parent_selector).each(function() {
var selectedClass = $("option:selected", this).val();
if (selectedClass) {
if (selected.length > 0) {
if (window.Zepto) {
/* Zepto class regexp dies with classes like foo\bar. */
selected += "\\\\";
} else {
selected += "\\";
}
}
selected += selectedClass;
}
});
/* Also check for first parent without subclassing. */
/* TODO: This should be dynamic and check for each parent */
/* without subclassing. */
var first;
if ($.isArray(parent_selector)) {
first = $(parent_selector[0]).first();
} else {
first = $(parent_selector).first();
}
var selected_first = $("option:selected", first).val();
$("option", child).each(function() {
/* Remove unneeded items but save the default value. */
if ($(this).hasClass(selected) && $(this).val() === currently_selected_value) {
$(this).prop("selected", true);
trigger_change = false;
} else if (!$(this).hasClass(selected) && !$(this).hasClass(selected_first) && $(this).val() !== "") {
$(this).remove();
}
});
/* If we have only the default value disable select. */
if (1 === $("option", child).size() && $(child).val() === "") {
$(child).prop("disabled", true);
} else {
$(child).prop("disabled", false);
}
if (trigger_change) {
$(child).trigger("change");
}
}
});
};
/* Alias for those who like to use more English like syntax. */
$.fn.chainedTo = $.fn.chained;
/* Default settings for plugin. */
$.fn.chained.defaults = {};
})(window.jQuery || window.Zepto, window, document);
$(document).ready(function(){
$("#connectionSub").chained("#connectionType");
$("#apppagesoptions").chained("#connectionSub");
$("#phoneVersion").chained("#phoneBrand");
$("#osVersionMin").chained("#phoneVersion");
$("#osVersionMax").chained("#phoneVersion");
});
and the HTML is 3 chained inputs:
<div class="Targeting">
<select id="connectionType" name="connectionType[]">
<option disabled value="">--</option>
<option value="pages">Facebook Pages</option>
<option value="apps">Apps</option>
<option disabled value="">--</option>
<option value="advanced">Advanced Connections</option>
</select>
<select id="connectionSub" name="connectionSub[]">
<option value="">--</option>
<option value="page_like" class="pages">People who like your Page</option>
<option value="page_friend" class="pages">Friends of people who like your Page</option>
<option value="page_exclude" class="pages">Exclude people who like your Page</option>
<option value="app_like" class="apps">People who like your App</option>
<option value="app_friend" class="apps">Friends of people who like your App</option>
<option value="app_exclude" class="apps">Exclude people who like your App</option>
</select>
<select id="apppagesoptions" name="apppagesoptions[]" multiple>
<option>test</option>
</select>
</div>
</div><!-- end #entry1 -->
<div id="addDelButtons">
<input type="button" id="TargetingAdd" value="add section">
<input type="button" id="TargetingDel" value="remove section above">
</div>
Appreciate any help with this!

For all the <div class="Targeting"> the select element's ids are remain same. that's why it's only working for first one.

Related

HTML conditional-based validation of the select option doesn't work

I would like to validate my select option list in HTML.
The full code is available here:
https://jsfiddle.net/crgfp8ud/
Where I want to distinguish the one option, which will make the other query below active.
I found a couple of solutions:
Jquery Conditional validation based on select text
https://www.py4u.net/discuss/962973
from where I've picked up the code and tried to adapt it for my purposes. Unfortunately they didn't work properly:
$(document).ready(function(){
$('input[name=proposed_cable_route]').validate({
rules: {
cname: { required: true },
select1: { valueNotEquals: "0" },
other: { required: function(element){
return $("#cf_proposed_cable_route option:selected").text() == "Internal";
}
}
}
});
and the other one
/*
$('select').change(function() {
var text = $('select option:selected').text();​​​​​​​​​
if(text == 'Internal') {
$('input[name=building_post_2000]').attr('disabled', true);
}
});
*/
the last one tried was:
$("input[name=proposed_cable_route]").on('select', function() {
var blockNumber = $('#cf_building_post_2000');
// if is company
if ($(this).val() == "Internal") {
// show panel
blockNumber.show();
// remove disabled prop
blockNumber.find('input,select,radio').prop('disabled', false);
} else {
// if is not company, hide the panel and
add disabled prop
//blockNumber.hide();
blockNumber.find("input").val("");
blockNumber.find('input,select,radio').prop('disabled', true);
}
});
Is there any way to make the validation based on the select option value?
I think this would do the trick. It gets the currently relevant SELECT and gets its value:
$('#cf_proposed_cable_route').change(function() {
var text = $(this).val();
console.log(text);
$('input[name=building_post_2000]').prop("disabled", text === 'Internal');
});
i think that you don't need to select event you just need to change event when you select one option the value of select tag will be change to be the same value of selected option
For Example
<select>
<option value="0">Select car:</option>
<option value="1">Audi:</option>
<option value="2">BMW</option>
<option value="3">Citroen</option>
<option value="4">Ford</option>
</select>
JS Validation
var sel = $("select");
sel.change(function () {
var opt = $("select option:checked")
if(opt.text() === "BMW") {
console.log(opt.text())
}
});
or you can create a loop
Example
var sel = $("select");
sel.change(function () {
for(var i = $("option").length; i >= 1; i--) {
if($(this).val() === $("option:nth-child("+i+")").val()) {
if($("option:nth-child("+i+")").text() === "BMW") {
console.log($("option:nth-child("+i+")").text())
}
}
}
});

How to reset or unselect multi select box using jQuery?

I have one bootstrap tab and i create multi select box using jQuery and the all functions are working properly but the RESET button only not working.
i try my all ways but its waste, anyone can you help me..
Please check my full code on fiddle,
MY FULL CODE IS HERE
Just want how to reset the field using jQuery
(function($) {
function refresh_select($select) {
// Clear columns
$select.wrapper.selected.html('');
$select.wrapper.non_selected.html('');
// Get search value
if ($select.wrapper.search) {
var query = $select.wrapper.search.val();
}
var options = [];
// Find all select options
$select.find('option').each(function() {
var $option = $(this);
var value = $option.prop('value');
var label = $option.text();
var selected = $option.is(':selected');
options.push({
value: value,
label: label,
selected: selected,
element: $option,
});
});
// Loop over select options and add to the non-selected and selected columns
options.forEach(function(option) {
var $row = $('<a tabindex="0" role="button" class="item"></a>').text(option.label).data('value', option.value);
// Create clone of row and add to the selected column
if (option.selected) {
$row.addClass('selected');
var $clone = $row.clone();
// Add click handler to mark row as non-selected
$clone.click(function() {
option.element.prop('selected', false);
$select.change();
});
// Add key handler to mark row as selected and make the control accessible
$clone.keypress(function() {
if (event.keyCode === 32 || event.keyCode === 13) {
// Prevent the default action to stop scrolling when space is pressed
event.preventDefault();
option.element.prop('selected', false);
$select.change();
}
});
$select.wrapper.selected.append($clone);
}
// Add click handler to mark row as selected
$row.click(function() {
option.element.prop('selected', 'selected');
$select.change();
});
// Add key handler to mark row as selected and make the control accessible
$row.keypress(function() {
if (event.keyCode === 32 || event.keyCode === 13) {
// Prevent the default action to stop scrolling when space is pressed
event.preventDefault();
option.element.prop('selected', 'selected');
$select.change();
}
});
// Apply search filtering
if (query && query != '' && option.label.toLowerCase().indexOf(query.toLowerCase()) === -1) {
return;
}
$select.wrapper.non_selected.append($row);
});
}
$.fn.multi = function(options) {
var settings = $.extend({
'enable_search': true,
'search_placeholder': 'Search...',
}, options);
return this.each(function() {
var $select = $(this);
// Check if already initalized
if ($select.data('multijs')) {
return;
}
// Make sure multiple is enabled
if (!$select.prop('multiple')) {
return;
}
// Hide select
$select.css('display', 'none');
$select.data('multijs', true);
// Start constructing selector
var $wrapper = $('<div class="multi-wrapper">');
// Add search bar
if (settings.enable_search) {
var $search = $('<input class="search-input" type="text" />').prop('placeholder', settings.search_placeholder);
$search.on('input change keyup', function() {
refresh_select($select);
})
$wrapper.append($search);
$wrapper.search = $search;
}
// Add columns for selected and non-selected
var $non_selected = $('<div class="non-selected-wrapper">');
var $selected = $('<div class="selected-wrapper">');
$wrapper.append($non_selected);
$wrapper.append($selected);
$wrapper.non_selected = $non_selected;
$wrapper.selected = $selected;
$select.wrapper = $wrapper;
// Add multi.js wrapper after select element
$select.after($wrapper);
// Initialize selector with values from select element
refresh_select($select);
// Refresh selector when select values change
$select.change(function() {
refresh_select($select);
});
});
}
})(jQuery);
$(document).ready(function() {
$('select').multi({
search_placeholder: 'Search',
});
});
/* Reset button */
function DeselectListBox() {
var ListBoxObject = document.getElementById("firstData")
for (var i = 0; i < ListBoxObject.length; i++) {
if (ListBoxObject.options[i].selected) {
ListBoxObject.options[i].selected = false
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You can trigger the click of your reset button and clear the whole div in your document ready function. After this you can remove the class "selected" so its completely reset.
Like this
$(document).ready(function() {
$('select').multi({
search_placeholder: 'Search',
});
$('#tabReset').click(function() {
$('.selected-wrapper').empty();
$('a').removeClass('selected');
});
});
attach an event to reset button. empty the selected-wrapper and remove the selected class from non-selected-wrapper
$("button.alltabreset").click(function(){
$(".selected-wrapper").empty();
$(".item").removeClass("selected");
});
solution: https://jsfiddle.net/zuov3wmb/

Change event triggering too early on select box

Here is a simplified version of my problem:
The HTML:
<select id="mySelect">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
The jQuery:
$('#mySelect').change( function() {
// do stuff
} );
The problem is that when I move my mouse cursor over the options, do stuff happens as I hover over one of the options, before I actually select the new option. How do I avoid this behaviour so that .change() is triggered only when I have finished choosing a new option in the select?
Edit 1: Further information
Apparently this code would not cause behaviour described. In the actual code the select boxes are being updated as further data is loaded via .get() and processed.
Edit 2: Actual function that updates a select box
This function is the one in my code that updates one of the select boxes after more data has loaded. The global variable padm_courses is an array of course objects, that have a code and name property used to populate the course filter select box.
function loadCourseFilter() {
var selected = '';
var sel = $('<select>').attr('id','padmCourseFilter');
$(padm_courses).each(function() {
sel.append($("<option>").attr('value',this.code).text(this.name));
});
if($('#padmCourseFilter').length) {
selected = $('#padmCourseFilter').val();
$('#padmCourseFilter').replaceWith(sel);
if(selected != '') $('#padmCourseFilter option[value="'+escape(selected)+'"]').prop('selected', true);
} else {
sel.appendTo('#padm_hub_filters');
}
$('#padmCourseFilter').change( function() {
processMCRsByCourse($('#padmCourseFilter').val());
var tables = $('.sv-datatable').DataTable();
tables.rows('.duplicate').remove().draw();
filterTheBlockFilter();
} );
}
Try changing your change event
$(document).on('change', '#mySelect', function() {
// do stuff
});
Okay, I found a solution. It seems that when triggered, the function loadCourseFilter was recreating the selectbox from scratch each time and overwriting the old one. This caused weird behaviour when hovering over one of the options.
A revised version of the function adds only new options, and does not update the filter if nothing was actually added...
function loadCourseFilter() {
var sel, output;
if($('#padmCourseFilter').length) {
var count = 0;
sel = $('padmCourseFilter');
output = [];
$(padm_courses).each(function() {
if($('#padmCourseFilter option[value="'+this.code+'"]').length == 0) {
count++;
output.push('<option value="'+this.code+'">'+this.name+'</option>');
}
});
if(count > 0) {
sel.append(output.join(''));
sortDropDownListByText('padmCourseFilter');
}
} else {
sel = $('<select>').attr('id','padmCourseFilter');
$(padm_courses).each(function() {
sel.append($("<option>").attr('value',this.code).text(this.name));
});
sel.appendTo('#padm_hub_filters');
}
$('#padmCourseFilter').change( function() {
processMCRsByCourse($('#padmCourseFilter').val());
var tables = $('.sv-datatable').DataTable();
tables.rows('.duplicate').remove().draw();
filterTheBlockFilter();
} );
}

Highlight a form select element if its value is empty

I have a form where people sign up for a shift. There are multiple shifts on the page, and I have an onchange event that sends the sign-up via Ajax.
<select name="user" class="assign_user" id="user1">
<option value="user1234" selected="selected">Joe Bloggs</option>
<option value="">No-one</option>
<option value="user1235">Jane Mahon</option>
<option value="user1236">Ahmed Kitab</option>
<option value="user1237">Dave Smew</option>
</select>
If <option value="">No-one</option> is selected, I want to highlight the select element in yellow.
I cannot think of a way to do this with just CSS. I tried
select[value=''] {
background-color:lightyellow;
}
but the attribute filter in the square brackets doesn't accept empty values, as far as I know.
EDIT: just to be clear, empty select elements should be highlighted when the page loads, as well as when the element changes
CSS
.empty {
background-color: lightyellow;
}
Javascript
function userChanged(select) {
select = $(select);
if (select.val() === "") select.addClass("empty");
else select.removeClass("empty");
}
// UPDATED: Initialization
$(function() {
var user1 = $("#user1");
user1.change(function() { userChanged(user1); });
// Highlights select when page loads.
userChanged(user1);
});
UPDATE: Future solution uses CSS only. From: Is there a CSS parent selector?
select:has(option:checked[value='']) {
background-color: lightyellow;
}
This is my solution
jQuery
$(document).ready(function() {
$('.assign_user').on('change', function() {
var user_number = $(this).val();
var shiftid = $(this).siblings('input[name=shiftid]').val();
/*send data to database via Ajax*/
$.ajax({
url: "/admin/saveshift",
type: "GET",
data: { 'action': 'update', 'shiftid': shiftid, 'user_number': user_number },
success: function()
{
console.log("added a shift ");
/* message to user to say the record is updated */
$('#assigned').html('Updated');
$('#assigned').show();
setTimeout(function () {
$('#assigned').fadeOut();
return false;
}, 10000);
}
});
/* if null is selected, add class unassigned */
if ($(this).val() == '') {
$(this).addClass('unassigned');
} else {
$(this).removeClass('unassigned');
}
});
/* when page loads, if null is selected, add class unassigned */
$('.assign_user').each(function() {
if ($(this).val() == '') {
$(this).addClass('unassigned');
}
});
});
CSS
select.unassigned {
background-color:lightyellow;
}
I couldn't think of a way to do this with just CSS, hence the jQuery.

Stop multiple select from scrolling to top when selection changes

I have a multi select list that i use to filter a grid with. When i select or deselect any item in the list, it triggers an event.
The problem is that when i scroll down and select or deselect a value lower on the list, it jumps back to the top. How can i stop this from happening?
I've made a fiddle: https://jsfiddle.net/Berge90/a2two97o/
Edit: Problems in Chrome. Works fine in Edge and Firefox
HTML
<div class="FilterContainer">
<select id="SelectTenant" multiple="true">
</select><br/>
<button onclick="setAllTenantSelections(true)">Select All</button>
<button onclick="setAllTenantSelections(false)">Select None</button>
</div>
Javascript
window.onload = setupFilter;
window.onload = populateTenantFilter;
gridHeaders = [{name: "Test"},{name: "Value"},{name: "Another one"},{name: "And another"},{name: "Last one"}];
//Selecting/Deselecting all values
function setAllTenantSelections(selected) {
var select = document.getElementById("SelectTenant");
for (element in select.children) {
select[element].selected = selected;
showCol(select[element].text, selected);
}
}
//Adding all values from array to list
function populateTenantFilter() {
var select = document.getElementById("SelectTenant");
select.innerHTML = "";
for (i = 0; i < gridHeaders.length; i++) {
var option = document.createElement("Option");
option.innerHTML = gridHeaders[i].name;
select.appendChild(option);
}
//setting all options as selected on load
setAllTenantSelections(true);
setupFilter();
}
//Adding onclick-events to the values
function setupFilter() {
$('select option').on('mousedown', function (e) {
this.selected = !this.selected;
if (this.selected) {
console.log("SELECTED: " + this.text);
showCol(this.text,true);
} else {
console.log("DESELECTED: " + this.text);
showCol(this.text,false);
}
e.preventDefault();
});
}
function showCol(){
//Filtering grid based on selection
}
I solved this by scrolling back to the original scrollTop in the next event loop:
$('.your-selector').mousedown(e => {
var el = e.target;
e.preventDefault();
// ...your code
// fixes erratic scroll behaviour in Chrome
var scrollTop = el.parentNode.scrollTop;
setTimeout(() => el.parentNode.scrollTo(0, scrollTop), 0);
});

Categories