I have a jQuery extension method to create custom animated drop-down select lists based on this answer. Using this method on a page with one drop-down works perfectly:
The extension method is as follows:
$.fn.extend({
slidingSelect: function (options) {
var select = $(this);
var selector = select.selector;
var width = $(selector).width();
var selectedValue = select.val();
if (selectedValue === "undefined")
selectedValue = select.find("option:first").val();
console.log($(selector + " option:selected").text());
var divToggle = $("<div class='SelectorToggle SelectorToggle-defualt'>" + $(selector + " option:selected").text() + "<button style='float: right; width: 20px' id='ddlImgBtn'></button></div>")
.attr({ id: select.attr("id") + "Toggle" })
.css({ width: select.width() + 20 })
.click(function () {
$(selector + "Toggle").toggleClass("SelectorToggle-defualt");
$(selector + "Toggle").toggleClass("SelectorToggle-pressed");
$(selector).slideToggle("fast");
}).insertBefore(select);
var optionCount = $(selector + " option").length;
if (optionCount < 5) {
select.attr("size", optionCount);
}
else {
select.attr("size", 5);
}
select.addClass("drop-down-selector");
$(selector).css({ 'width': select.width() + 20, 'position': 'absolute', "z-index": '9999' });
select.change(function () {
divToggle.html($(selector + " option:selected").text() + "<button style='float: right; width: 20px' id='ddlImgBtn'></button>");
$(selector + "Toggle").toggleClass("SelectorToggle-defualt");
$(selector + "Toggle").toggleClass("SelectorToggle-pressed");
$(selector).slideToggle("fast");
});
}
});
I call it as follows:
$("#LanguageSelector").hide().slidingSelect();
I am however having endless issues getting it to work on a page with multiple drop-downs. My dropdowns are dynamically created as part of a DataTable solution with server-side processing. The drop-downs in the footer:
If i call the following:
$("select").hide().slidingSelect();
then somehow all drop-downs on the page create the custom control:
if I attempt to call the extension method on each element individually:
$("select").hide().each(function(index) {
$(this).slidingSelect();
});
I also tried to call the extension method individually as the drop-downs are created (to just one of them):
$('#RelatedCasesGrid tfoot th').each(function () {
var col = $(this).html();
//..........
else if (col === "ComplaintTypeName") {
$(this).html(GetDropDownInput(col, caseId));
var element = $(this).find("select");
element.hide().slidingSelect();
}
The method GetDropDownInput(col, caseId) creates the drop-downs as follows:
function GetDropDownInput(col, id) {
var control;
$.ajax({
method: "GET",
dataType: "json",
async: false,
url: "/OATS/Api/GetColumnItems/" + id + "?column=" + col
}).done(function (data) {
control = "<select id='selector' class='table-filter-input-drop-down-list'><option value='' disabled selected>Filter by</option>"
for (var i = 0; i < data.length; i++) {
control += "<option col-type=" + data[i].Type + " value='" + data[i].Name + "'";
if (data[i].Selected) {
control += "selected='selected'";
}
control += ">" + data[i].Name + "</option>";
}
control += "</select>";
});
return control;
}
The result of this:
From: http://www.w3schools.com
The id attribute specifies a unique id for an HTML element (the value
must be unique within the HTML document).
But your ajax method create the same id for all selects: "selector". Change this method to create unique id (value of 'col' parameter seems be ok for this purpose), and then call:
$("#your_unique_id").hide().slidingSelect();
Related
Currently I'm using checkmarx to find vulnerabilities on mi code.
The javascript files aparently haev some potential xss vulnerabilites when I use jquery val() function and then try to append this val. How should I solve, sanitize or encode this to avoid this problem?
Here some examples about what checkmarx mark as vulnerability:
function insertContactToTable(table) {
var ContactId = jQuery("#select_contacts").val();
var ContactName = jQuery("#select_contacts option:selected").text();
var Type = jQuery("#select_contact_type").val();
if (ContactId != "" && Type != "") {
var ID = ContactId + "_" + Type;
var Img = "<img class='image pointer-item' src='/app/assets/img/icon-package/cross.png' alt='cross' onClick='removeTableLine(\"" + ID + "\")'/>";
if (jQuery("#" + table + " tbody tr:last").length > 0) {
jQuery("#" + table + " tbody tr:last").after("<tr id='" + ID + "' name='" + ID + "'><td id='" + ID + "' name='contact_list'>" + ContactName + "</td><td>" + Type + "</td><td>" + Img + "</td></tr>");
} else {
jQuery("#" + table + " tbody").html("<tr id='" + ID + "' name='" + ID + "'><td id='" + ID + "' name='contact_list'>" + ContactName + "</td><td>" + Type + "</td><td>" + Img + "</td></tr>");
}
}
...
It marks the following error:
The application's insertContactToTable embeds untrusted data in the generated output with after, at line 542 of app\assets\js\administration.js. This untrusted data is embedded straight into the output without proper sanitization or encoding, enabling an attacker to inject malicious code into the output.
The line 542 is the jQuery("#select_contacts").val(); but it happens the same with the others lines that use .val() and .text() function.
Also, on other functions happens the same while getting this .val() or .text() functions and trying to use them with append() or html() functions.
Finally, I also have same issue while getting ajax response and try to append it with append() o html().
Note: I'm using php on my project, sanitizing most of the variables with it.
Edit
I changed to DOM object as suggested in comments and the code now looks like this:
var ContactId = jQuery("#select_contacts").val();
var ContactName = jQuery("#select_contacts option:selected").text();
var Type = jQuery("#select_contact_type").val();
if (ContactId != "" && Type != "") {
var ID = ContactId + "_" + Type;
var Img = jQuery("<img>", { "class": 'image pointer-item', alt: 'cross', "src": '/app/assets/img/icon-package/cross.png'
}).on("click", function() {
removeTableLine(ID);
});
var row = $("<tr>", { id:"TR_" +ID , name: ID })
.append($("<td>", { id: ID, name: 'contact_list', text: ContactName }))
.append($("<td>", { text: Type }))
.append($("<td>").append(Img));
$("#" + table + " tbody").append(row);
}
but I still have the problem
Don't create a string of HTML, create a DOM object and set its attributes.
if (ContactId != "" && Type != "") {
var ID = ContactId + "_" + Type;
var Img = jQuery("<img>", {
"class": 'image pointer-item',
alt: 'cross'
}).click(function() {
removeTableLine(ID);
});
var row = $("<tr>", {
id: ID,
name: ID
})
.append($("<td>", {
id: ID,
name: 'contact_list',
text: ContactName
}))
.append($("<td>").append(Img))
$("#" + table + " tbody").append(row);
}
You don't need different code depending on whether there's already a last row or not. Just append the new row to the table body.
You have another problem, you're using the same ID for the <tr> and first <td> in the row. If you really need them both to have an ID, they should have different IDs.
Use the DOMPurify library to sanitize the val() and text() return values before .append-ing it to the DOM
var ContactId = DOMPurify.sanitize(jQuery("#select_contacts").val());
var ContactName = DOMPurify.sanitize(jQuery("#select_contacts option:selected").text());
var Type = DOMPurify.sanitize(jQuery("#select_contact_type").val());
if (ContactId != "" && Type != "") {
var ID = ContactId + "_" + Type;
var Img = jQuery("<img>", { "class": 'image pointer-item', alt: 'cross', "src": '/app/assets/img/icon-package/cross.png'
}).on("click", function() {
removeTableLine(ID);
});
var row = $("<tr>", { id:"TR_" +ID , name: ID })
.append($("<td>", { id: ID, name: 'contact_list', text: ContactName }))
.append($("<td>", { text: Type }))
.append($("<td>").append(Img));
$("#" + table + " tbody").append(row);
}
If there are attributes or tags you want to be allowed, you can pass a second parameter to the sanitize function to define that whitelist:
// allow only <b> and <q> with style attributes
var clean = DOMPurify.sanitize(dirty, {ALLOWED_TAGS: ['b', 'q'], ALLOWED_ATTR: ['style']});
I have a list of tables.
var tables = "";
for (var i = 0; i <= data.length - 1; i++) {
if (data[i].current_order == null) {
tables += '<button class="table_btn" value="' + data[i].id + '">' + data[i].table_number + '</div>';
} else {
tables += '<button class="table_selected" key="' + data[i].current_order + '"value="' + data[i].id + '">' + data[i].table_number + '</div>';
}
And tables have two color, when it is busy or not busy. If there is current_order in table, it shows busy. What I want to do is that when a user clicks empty table, it gets table_id, changes class from table_btn to table_selected and add keyof div, which is current_order.
I use phoenix-framework for my backend, so when a user clicks an empty table, it creates order and passes value of clicked table_id and created order_id. But I am not sure that how can I get a table by value of table div and put key into div...
Can anyone give me advice for this??
So as you tagged Jquery, i'm gonna post this. Change key for ID and you can do the following. I would then wrap the add table and remove table in functions where u pass in the data[i].current_order into and use that.
Edited based on user feeback, not tested
/*If you are not comfortable using the variable 'This',
you can just pass in the id of target table and
change the syntax to $("#"+targetTable)*/
var tables = "";
for (var i = 0; i <= data.length - 1; i++) {
if (data[i].current_order == null) {
tables += '<button class="table_btn" value="' + data[i].id + '">' + data[i].table_number + '</div>';
} else {
tables += '<button class="table_selected" id="' + data[i].current_order + '"value="' + data[i].id + '">' + data[i].table_number + '</div>';
}
// On Click set table to busy
$(".table_btn").click(function(){
addTable($(this).val(), $(this));
});
// Add table function
function addTable(tableId, targetTable){
$.ajax({
url: "YourBackEndHere",
data: tableID
cache: false,
success: function(html){
$(targetTable).removeClass("table_btn");
$(targetTable).addClass("table_selected");
$(targetTable).attr("id", data[i].current_order);
}
});
}
// On click set table to empty
$(".table_selected").click(function(){
removeTable($(this).val(), $(this));
});
// Remove table function
function removeTable(tableId, targetTable){
$.ajax({
data: tableId
url: "YourBackEndHere",
cache: false,
success: function(html){
$(targetTable).removeClass("table_selected");
$(targetTable).addClass("table_btn");
$(targetTable).attr("id", "");
});
}
});
}
I am extremely new at writing ajax and working with a restful api... so, bear with me.
I have a Laravel 5.2 RESTful API that I am using on the backend, and I'm attempting to simply load a list of categories using Jquery / Ajax. As you click through the categories, each child category loads fine, but I cannot seem to get the "back" button to work (by this, I mean the LI I am generating, not the browser back button). When you click it, it shows the alert - and the data is correct, but that's it. The list doesn't refresh and populate with the appropriate items.
EDIT
There are no errors being thrown to the javascript console either. It just won't populate from the ajax call.
EDIT
I removed the request.abort() right after I made the original post.
EDIT
Here is the JSON returned from the URL api/categories/4 - as an example.
[{"id":6,"parent":4,"name":"sub_subcat4_1","slug":"sub_subcat4_1","description":null,"created_at":null,"updated_at":null},{"id":7,"parent":4,"name":"sub_subcat4_2","slug":"sub_subcat4_2","description":null,"created_at":null,"updated_at":null}]
EDIT
Here is the HTML for the #categories
<div class="row">
<div class="col-sm-12">
<ul id="categories">
</ul>
</div>
The Javascript
<script>
/*
* This loads the default / root categories.
*/
function getRootCategories() {
$.getJSON("api/categories", function(data) {
var categories = [];
$("#categories").html("");
$.each(data, function(key, val) {
$("#categories").append("<li class='subcat' data-id='" + val.id + "' onClick='getSubcats(this);'>" + val.name + '</li>');
});
});
}
/*
* This loads the sub categories if there's any data returned. Otherwise, just leave the user where they are.
*/
function getSubcats(cat) {
var dataID = cat.getAttribute("data-id");
alert(dataID);
if(dataID == "null") {
getRootCategories();
}
else {
$.getJSON("api/categories/" + dataID, function (data) {
if (data.length != 0) {
$("#categories").html("");
var newCats = '';
var parent = '';
$.each(data, function (key, val) {
parent = "<li class='subcat' data-id='" + val.parent + "' onClick='getSubcats(this);'>Back</li>";
newCats += "<li class='subcat' data-id='" + val.id + "' onClick='getSubcats(this);'>" + val.name + '</li>';
});
$("#categories").append(parent + newCats);
}
});
}
}
$(document).ready(function() {
$.ajaxSetup({ cache:false });
getRootCategories();
});
</script>
Ok, I just had my variables all mixed up. I wasn't setting the correct parent id.
The new script looks like this -
<script>
var previous = null;
/*
* This loads the default / root categories.
*/
function getRootCategories() {
$.getJSON("api/categories", function(data) {
var categories = [];
$("#categories").html("");
$.each(data, function(key, val) {
$("#categories").append("<li class='subcat' data-parent='" + val.parent + "' data-id='" + val.id + "' onClick='getSubcats(this);'>" + val.name + '</li>');
previous = val.parent;
});
});
}
/*
* This loads the sub categories if there's any data returned. Otherwise, just leave the user where they are.
*/
function getSubcats(cat) {
var dataID = cat.getAttribute("data-id");
previous = cat.getAttribute("data-parent");
if(dataID == "null") {
getRootCategories();
}
else {
$.getJSON("api/categories/" + dataID, function (data) {
if (data.length != 0) {
$("#categories").html("");
var newCats = '';
var parent = '';
$.each(data, function (key, val) {
parent = "<li class='subcat' data-id='" + previous + "' onClick='getSubcats(this);'>Back</li>";
newCats += "<li class='subcat' data-parent='" + val.parent + "' data-id='" + val.id + "' onClick='getSubcats(this);'>" + val.name + '</li>';
});
$("#categories").append(parent + newCats);
}
})
.fail(function(jqxhr, textStatus, error) {
console.log("Request Failed: " + textStatus + " - " + error);
});
}
}
$(document).ready(function() {
$.ajaxSetup({ cache:false });
getRootCategories();
});
</script>
function demo(){
$('.box').slideToggle('fast');
}
$(document).ready(function(){
$.getJSON( "js/JobOpenings.json", function( data ) {
var glrScrlImg = [];
$.each( data.getJobOpeningsResult, function( key, val ) {
var st = "",id,st2= "",st3="",id;
st +="<h4>" + val.JobTitle + "</h4>";
st3 += "<div class='box'>" + val.JobDetails + "</div>";
$("#newsDetails").append("<li onclick='demo()'>" + st+val.JobSector + "<br>" + st3 + "</li>");
$('.box').hide();
});
});
});
I am reading data from a json file. The div with 'box' class is hidden. Currently this code is displaying all div on click of the li. What changes should I make to display only the div corresponding to the clicked li?
Here what we need to do is to find the .box element within the clicked li, so we need to get a reference to the clicked element.
I would use a delegated jQuery event handler with css to initially hide the element
$(document).ready(function () {
$('#newsDetails').on('click', 'li', function () {
$(this).find('.box').toggleClass('hidden');
})
$.getJSON("js/JobOpenings.json", function (data) {
var glrScrlImg = [];
$.each(data.getJobOpeningsResult, function (key, val) {
var st = "",
id, st2 = "",
st3 = "",
id;
st += "<h4>" + val.JobTitle + "</h4>";
st3 += "<div class='box hidden'>" + val.JobDetails + "</div>";
$("#newsDetails").append("<li>" + st + val.JobSector + "<br>" + st3 + "</li>");
});
});
});
with css
.hidden {
display: none;
}
Pass the control to the function and then based on your control slideToggle its respective .box
function demo(ctrl){
$(ctrl).find('.box').slideToggle('fast');
}
$(document).ready(function(){
$.getJSON( "js/JobOpenings.json", function( data ) {
var glrScrlImg = [];
$.each( data.getJobOpeningsResult, function( key, val ) {
var st = "",id,st2= "",st3="",id;
st +="<h4>" + val.JobTitle + "</h4>";
st3 += "<div class='box'>" + val.JobDetails + "</div>";
$("#newsDetails").append("<li onclick='demo(this)'>" + st+val.JobSector + "<br>" + st3 + "</li>");
$('.box').hide();
});
});
});
Or add a class to li and attach an event handler like below instead of writing inline onclick as below:
$("#newsDetails").append("<li class="someclass"'>" + st+val.JobSector + "<br>" + st3 + "</li>");
and then instead of function demo() write this
$('#newsDetails').on('click','.someclass',function(){
$(this).find('.box').slideToggle('fast');
});
UPDATE
Method 1:
function demo(ctrl){
$('#newsDetails').find('li.box').hide('fast'); //hide all the .box
$(ctrl).find('.box').slideToggle('fast');
}
Method 2:
$('#newsDetails').on('click','.someclass',function(){
$('#newsDetails').find('li.box').hide('fast'); //hide all the .box
$(this).find('.box').slideToggle('fast');
});
UPDATE 2:
Method 1:
function demo(ctrl){
$('#newsDetails').find('li.box').not($(ctrl).find('.box')).hide('fast'); //hide all the .box
$(ctrl).find('.box').slideToggle('fast');
}
Method 2:
$('#newsDetails').on('click','.someclass',function(){
$('#newsDetails').find('li.box').not($(ctrl).find('.box')).hide('fast'); //hide all the .box except this
$(this).find('.box').slideToggle('fast');
});
You should structure your html (which is missing from the question!) so that the div and li are "connected" in some way (maybe the div is child of li, or they have same class, ecc).
Right now the line
$('.box').slideToggle('fast');
is applied to all element with class '.box' in your page. You want to be more selective there, that's where the way you structure the html comes into play.
Here's an example: http://jsfiddle.net/owe0faLs/1/
So i have a page that has a couple of jQuery plugins. Among other things i have the multiselect toolbar, a pretty sweet plugin. problem is that when i load up the page in internet explorer the page breaks. i've been able to determine that the problem occurs when i try to set some attributes to some elements that i have dynamically generated.
here is the code for generating the elements:
$.ajax({
url: '#Url.Content("~")' + 'Ticket/GetTvrtke',
async: false,
success: function (data) {
document.getElementById("header_tvrtka_holder").innerHTML = data;
var tvrtke = data.split(", ");
for (var i = 0; i < tvrtke.length; i++) {
document.getElementById("KlijentMultiSelect").innerHTML +=
"<option value=\"" + tvrtke[i] + "\" id=\"" + tvrtke[i] + "\" >" + tvrtke[i] + "</option>";
}
$("#KlijentMultiSelect").multiselect({
selectedText: "",
height: 125,
minWidth: 650,
noneSelectedText: 'Izaberite željene tvrtke:'
});
}
});
the function gets the correct data and generates the options, and then i activate the plugin to render the new dropdown menu with checkboxes.
problem is that afterwards i have this code:
var tvrtke = document.getElementById("header_tvrtka_holder").innerHTML.split(", ");
for (var i = 0; i < tvrtke.length; i++) {
document.getElementById("ui-multiselect-" + tvrtke[i]).checked = true;
}
for (var i = 0; i < tvrtke.length; i++) {
document.getElementById("ui-multiselect-" + tvrtke[i]).setAttribute("onclick", "ChangeTextKlijent()");
}
here i am trying to set the checkbox values to true and add an on click event to the input element, but then visual studio sends me the error message from the title. in firefox, everything works great but IE is a whole different story.
anyone know how to fix this?
I switched so that my code looks like this:
var tvrtke = document.getElementById("header_tvrtka_holder").innerHTML.split(", ");
$.each(tvrtke, function (index, value) {
$("#KlijentMultiSelect").append("<option value=\"" + value + "\" id=\"" + value + "\" >" + value + "</option>");
});
and now it works.