How to optimize and minify the following code - javascript

I am attaching an excerpt from my code:
document.getElementById("product-selector-25b").onchange = function () {
document.getElementById("purchase-25b").href = "https://example.com/?add-to-cart=" + this.selectedOptions[0].getAttribute('data-product') + "&" + "variation_id=" + this.value + "/";
}
document.getElementById("product-selector-26").onchange = function () {
document.getElementById("purchase-26").href = "https://example.com/?add-to-cart=" + this.selectedOptions[0].getAttribute('data-product') + "&" + "variation_id=" + this.value + "/";
}
document.getElementById("product-selector-29").onchange = function () {
document.getElementById("purchase-29").href = "https://example.com/?add-to-cart=" + this.selectedOptions[0].getAttribute('data-product') + "&" + "variation_id=" + this.value + "/";
}
document.getElementById("product-selector-Zx3").onchange = function () {
document.getElementById("purchase-Zx3").href = "https://example.com/?add-to-cart=" + this.selectedOptions[0].getAttribute('data-product') + "&" + "variation_id=" + this.value + "/";
}
document.getElementById("product-selector-001sfF").onchange = function () {
document.getElementById("purchase-001sfF").href = "https://example.com/?add-to-cart=" + this.selectedOptions[0].getAttribute('data-product') + "&" + "variation_id=" + this.value + "/";
}
As you can see, the code is repeated in everything except IDs getElementById("product-selector-25b") purchase-25b
The problem is that in total I have hundreds of different IDs and I believe that this code can be optimized somehow, but I don’t understand how. What do you advise?

Here is how you can refacto your code :
const selectors = [
"product-selector-25b",
"product-selector-26",
"product-selector-29",
"product-selector-Zx3",
"product-selector-001sfF"
];
const updateLink = (e) => {
const selectorId = e.target.id;
const productId = e.target.selectedOptions[0].getAttribute("data-product");
const variationId = e.target.value;
const purchaseId = `purchase-${selectorId.split("-")[1]}`;
document.getElementById(purchaseId).href = `https://example.com/?add-to-cart=${productId}&variation_id=${variationId}/`;
};
document.addEventListener("DOMContentLoaded", () => {
selectors.forEach(selectorId => {
const element = document.getElementById(selectorId);
if(element){
element.onchange = updateLink;
}else {
console.error(`Element with id ${selectorId} not found`)
}
});
});
Just add others ids to the array.
When you want to optimize a repetitive code, the idea is to create a reusable function and loop inside of it

My solution is this:
var elements = document.querySelectorAll('[id^="product-selector-"]');
var updateLink = function(el) {
var tagId = el.id.replace("product-selector-", "");
document.getElementById("purchase-" + tagId).href = "https://example.com/?add-to-cart=" + this.selectedOptions[0].getAttribute('data-product') + "&" + "variation_id=" + this.value + "/";
}
for (var i=0; i < elements.length; i++) {
elements[i].addEventListener("change", function(){
updateLink(this);
});
}
Note: I assume all IDs start with "product-selector-".
Otherwise you need to create an array which contains all the IDs.

Related

Replacing null with 0 when summing values

I have a function that sums my values and everything works fine, but only when all inputs have a value entered. However, I would like default to have 0 assigned to it.so that the function works when at least one value is given . How to do it ?.
var DeductiblepercentageM = thisPointer.entity.getValue('number.DeductiblepercentageM[' + i + ']');
var InsuranceLimitM = thisPointer.entity.getValue('number.InsuranceLimitM[' + i + ']');
var insuranceRaitingM = thisPointer.entity.getValue('number.insuranceRaitingM[' + i + ']');
var InsurerNumberM = thisPointer.entity.getValue('number.InsurerNumberM[' + i + ']');
DeductiblepercentageM = DeductiblepercentageM.replace(",", ".");
DeductiblepercentageM = parseFloat(DeductiblepercentageM)
InsuranceLimitM = InsuranceLimitM.replace(",", ".");
InsuranceLimitM = parseFloat(InsuranceLimitM)
insuranceRaitingM = insuranceRaitingM.replace(",", ".");
insuranceRaitingM = parseFloat(insuranceRaitingM)
InsurerNumberM = InsurerNumberM.replace(",", ".");
InsurerNumberM = parseFloat(InsurerNumberM)
//log the outcome of decimal separator change
var positionSum = +(DeductiblepercentageM + InsuranceLimitM +insuranceRaitingM + InsurerNumberM);
jQ('[id="DeductibleM[' + i + ']"]').val(positionSum);
thisPointer.entity.setValue('DeductibleM[' + i + ']', positionSum);
thisPointer.entity.mergeLocal(true);
if (totalSum != "NaN") {
totalSum = +(totalSum + positionSum).toFixed();
}
}
else {
totalSum = "-";
}
According to #Terry
var InsuranceLimitM = thisPointer.entity.getValue('number.InsuranceLimitM[' + i + ']') || 0;
adding || 0 to the end of the code helps and makes the code count right away

Is there a work around to reference a variable above where its declared

So I'm building a template with handlebars and I want to append const totalSum to const sumTextSidebar. But obviously it can't find it bc it hasn't been created yet. Is there a workaround for this? I included my code below. Thank you!
function multi(element) {
const responseSidebar = ($(element).data('sidebar-respond'));
const responseUnder = ($(element).data('response-under'));
const optionId = Number($(element).data('optionid')) + 1;
const questionId = $(element).data('questionid') + 1;
const responseUnderArea = '#quiz-alert-' + questionId;
const sidebarId = '#sidebar-' + (questionId - 1) + '-' + (optionId - 1);
const sidebarQuestion = '#sidebar-' + (questionId - 1);
const responseUnderID = "#" + (questionId - 1) + "-" + (optionId - 1);
const sumTextUnderQuestionID = '#sumTextUnder-' + (questionId - 1);
**const sumTextSidebar** = ($(element).data('sumTextSidebar')+ totalSum);
const sumSidebarElement = $('<li>' + sumTextSidebar + '</li>');
const values = Number($(element).val());
const sumSidebarId = '#sumTextID-' + questionId;
if ($(element).is(':checked')) {
$(sidebarId).removeClass('hidden');
$(responseUnderID).removeClass('hidden');
$(responseUnderArea).removeClass('hidden');
$(sumTextUnderQuestionID).removeClass('hidden');
if ($(sumSidebarId).length == 0) {
$(sidebarQuestion).append(sumSidebarElement).slideDown('slow');
sumSidebarElement.attr('id', 'sumTextID-' + questionId);
}
addValue.push(values)
} else {
$(sidebarId).addClass('hidden');
$(responseUnderID).addClass('hidden');
subtractValue.push(values)
}
function getSum(total, num) {
return total + num;
}
const addValueSum = addValue.reduce(getSum);
const subtractValueSum = subtractValue.reduce(getSum);
**const totalSum** = addValueSum - subtractValueSum;

Preview not display straight away

When I click on my button's at top of textarea it opens either a hyperlink modal or a image insert modal shown in codepen example below.
I can create and insert links and images fine.
How ever when I create the links or image and click save on modal the preview does not show straight away I have to press a key in textarea to see new changes in preview.
Question: When I add a new hyperlink or image from my bootstrap modal
and click save how can I make sure the changes show up in preview
straight away. Using showdown.js
CODEPEN EXAMPLE
Script
$("#message").on('keyup paste copy change', function() {
var text = document.getElementById('message').value,
target = document.getElementById('showdown'),
converter = new showdown.Converter({parseImgDimensions: true}),
html = converter.makeHtml(text);
target.innerHTML = html;
});
Full Script
$('#button-link').on('click', function() {
$('#myLink').modal('show');
});
$('#button-image').on('click', function() {
$('#myImage').modal('show');
});
$('#button-smile').on('click', function() {
$('#mySmile').modal('show');
});
$('#myLink').on('shown.bs.modal', function() {
var textarea = document.getElementById("message");
var len = textarea.value.length;
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var selectedText = textarea.value.substring(start, end);
$('#link_title').val(selectedText);
$('#link_url').val('http://');
});
$('#myImage').on('shown.bs.modal', function() {
$("#image_url").attr("placeholder", "http://www.example.com/image.png");
});
$("#save-image").on('click', function(e) {
var textarea = document.getElementById("message");
var len = textarea.value.length;
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var selectedText = textarea.value.substring(start, end);
var counter = findAvailableNumber(textarea);
var replace_word = '![enter image description here]' + '[' + counter + ']';
if (counter == 1) {
if ($('input#image_width').val().length > 0) {
var add_link = '\n\n' + ' [' + counter + ']: ' + $('#image_url').val() + ' =' + $('input#image_width').val() + 'x' + $('input#image_height').val();
} else {
var add_link = '\n\n' + ' [' + counter + ']: ' + $('#image_url').val();
}
} else {
var add_link = '\n' + ' [' + counter + ']: ' + $('#image_url').val();
}
textarea.value = textarea.value.substring(0, start) + replace_word + textarea.value.substring(end,len) + add_link;
});
$("#save-link").on('click', function(e) {
var textarea = document.getElementById("message");
var len = textarea.value.length;
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var selectedText = textarea.value.substring(start, end);
var counter = findAvailableNumber(textarea);
if ($('#link_title').val().length > 0) {
var replace_word = '[' + $('#link_title').val() + ']' + '[' + counter + ']';
} else {
var replace_word = '[enter link description here]' + '[' + counter + ']';
}
if (counter == 1) {
var add_link = '\n\n' + ' [' + counter + ']: ' + $('#link_url').val();
} else {
var add_link = '\n' + ' [' + counter + ']: ' + $('#link_url').val();
}
textarea.value = textarea.value.substring(0, start) + replace_word + textarea.value.substring(end,len) + add_link;
});
function findAvailableNumber(textarea){
var number = 1;
var a = textarea.value;
if(a.indexOf('[1]') > -1){
//Find lines with links
var matches = a.match(/(^|\n)\s*\[\d+\]:/g);
//Find corresponding numbers
var usedNumbers = matches.map(function(match){
return parseInt(match.match(/\d+/)[0]); }
);
//Find first unused number
var number = 1;
while(true){
if(usedNumbers.indexOf(number) === -1){
//Found unused number
return number;
}
number++;
}
}
return number;
}
$("#message").on('keyup paste copy change', function() {
var text = document.getElementById('message').value,
target = document.getElementById('showdown'),
converter = new showdown.Converter({parseImgDimensions: true}),
html = converter.makeHtml(text);
target.innerHTML = html;
});
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
Just trigger the keyup event at the end of the $("#save-link").on('click', function(e) {});
I assume as jQuery setting value doesn't trigger any of the related events set on $("#message")
$("#message").trigger('keyup');
Just tested on the codepen and works fine,
$("#save-link").on('click', function(e) {
//All your code
// ....
$("#message").trigger('keyup');
});
I hope this helps !!

Passing scope as parameter into function to be saved

I have two functions in my controller found below.
$scope.formatPaymentDates = function() {
$scope.formatDate($scope.payment.due_date);
$scope.formatDate($scope.payment.date);
};
$scope.formatDate = function(attr) {
if (attr) {
var split_date = attr.split("-");
if (split_date[0].length == 4) {
attr = split_date[1] + "-" + split_date[2] + "-" + split_date[0];
} else {
attr = split_date[2] + "-" + split_date[0] + "-" + split_date[1];
}
}
};
The problem is that the function formatDate only sets the scope variable attribute and does not save it. What am I missing?
In JavaScript, strings and numbers are passed by value, while objects are passed by reference. You should pass an object as the parameter.
Or, as #faby mentioned, return the value:
$scope.formatPaymentDates = function() {
$scope.payment.due_date = $scope.formatDate($scope.payment.due_date);
$scope.payment.date = $scope.formatDate($scope.payment.date);
};
$scope.formatDate = function(attr) {
var result;
if (attr) {
var split_date = attr.split("-");
if (split_date[0].length == 4) {
result = split_date[1] + "-" + split_date[2] + "-" + split_date[0];
} else {
result = split_date[2] + "-" + split_date[0] + "-" + split_date[1];
}
}
return result;
};
the problem is that you aren't returning any result from your formatDate function.
You are manipulating your variable inside the function and then destroy it.
try this
$scope.formatDate = function(attr) {
if (attr) {
var split_date = attr.split("-");
if (split_date[0].length == 4) {
attr = split_date[1] + "-" + split_date[2] + "-" + split_date[0];
} else {
attr = split_date[2] + "-" + split_date[0] + "-" + split_date[1];
}
return attr;
}
return null
};
and then
$scope.formatPaymentDates = function() {
$scope.payment.due_date= $scope.formatDate($scope.payment.due_date);
$scope.payment.date=$scope.formatDate($scope.payment.date);
};

Cannot collect alle results of asynchronous call with geo.getLocations (Google Maps)

Below is my code. The problem is that recordsOut[0] is always undefined, whatever I try.
I know it has to do with the callback result. I tried to add some delays to give it more time to give a result back, but that did not help.
Any idea (with example please)? Very much appreciated.
function getAddress(id, searchValue) {
geo.getLocations(searchValue, function(result) {
if (result.Status.code == G_GEO_SUCCESS) {
var recordsOutStr = id + ';' + searchValue + ';';
for (var j = 0; j < result.Placemark.length; j++)
recordsOutStr += result.Placemark[j].address + ';' + result.Placemark[j].Point.coordinates[0] + ';' + result.Placemark[j].Point.coordinates[1];
recordsOut.push(recordsOutStr);
alert(recordsOutStr);
}
else {
var reason = "Code " + result.Status.code;
if (reasons[result.Status.code])
reason = reasons[result.Status.code]
alert('Could not find "' + searchValue + '" ' + reason);
}
});
}
function delay(ms)
{
var date = new Date();
var curDate = null;
do
{
curDate = new Date();
}
while (curDate - date < ms);
}
function processData()
{
objDataIn = document.getElementById("dataIn");
objDataOut = document.getElementById("dataOut");
if (objDataIn != null)
{
//alert(objDataIn.value);
if (objDataOut != null) {
recordsIn = explode(objDataIn.value, ';', true);
//for (i = 0; i < recordsIn.length; i++)
for (i = 0; i <= 5; i++)
{
addressStr = recordsIn[i]['address'] + ', ' +
recordsIn[i]['postalcode'] + ' ' +
recordsIn[i]['city'] + ', ' +
recordsIn[i]['country'];
getAddress(recordsIn[i]['id'], addressStr); //This will set resultStr
delay(200);
}
delay(5000);
alert('***' + recordsOut[0] + '***');
alert('***' + recordsOut[1] + '***');
alert('***' + recordsOut[2] + '***');
alert('***' + recordsOut[3] + '***');
alert('***' + recordsOut[4] + '***');
}
}
document.frmGeoCoder.submit();
}
Make sure that you have already defined recordsOut like this:
var recordsOut = [];
If you do it like this - var recordsOut; - it will be undefined.
If that doesn't work for you, please post the rest of the code, so we can see exactly what's going on.

Categories