Understanding “this” keyword in JavaScript.

‘Hi Everyone!

In this article, we are about to discuss  ‘this’ keyword in JavaScript. It is a very important part of object-oriented JS programming. Hence it becomes crucial for serious developers to understand  ‘this’ keyword.

It is also one of the most confused concepts of JavaScript.

What is ‘this’?

Understanding 'this':

To be able to understand what I am about to talk, you should be familiar with the basics of JavaScript. e.x – Variables, Objects, Functions, etc…

There isn’t a single word that describes ‘this' well, so I just think of it as a special variable that changes depending on the situation. Those different situations are captured below.

case 1:

In a regular function (or if you’re not in a function at all), 'this' points to'window' This is the default case.

function logThis() {
console.log(this);
}

logThis(); // window

// In strict mode, `this` will be `undefined` instead of `window`.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

Case 2:

When a function is called as a method, 'this' points to the object that’s on the left side of the dot.

/*
 * You can also think of this as the "left of the dot" rule. 
 * For example, in myObject.myMethod(), `this` will be myObject
 * because myObject is to the left of the dot.
 *
 * Of course, if you're using this syntax myObject['myMethod'](),
 * technically it would be the "left of the dot or bracket" rule,
 * but that sounds clumsy and generally terrible.
 *
 * If you have multiple dots, the relevant dot is the one closest 
 * to the method call. For example, if you have one.two.hi();
 * `this` inside of hi will be two.
 */

var myObject = {
  myMethod: function() {
    console.log(this);
  }
};

myObject.myMethod(); // myObject

Case 3:

In a function that’s being called as a constructor, 'this' points to the object that the constructor is creating.

function Person(name) {
  this.name = name;
}

var gordon = new Person('gordon');
console.log(gordon); // {name: 'gordon'}

Case 4:

When you explicitly set the value of 'this' manually using,'bind''apply' or,'call' it’s all up to you.

function logThis() {
  console.log(this);
}

var explicitlySetLogThis = logThis.bind({name: 'Gordon'});

explicitlySetLogThis(); // {name: 'Gordon'}

// Note that a function returned from .bind (like `boundOnce` below),
// cannot be bound to a different `this` value ever again.
// In other words, functions can only be bound once.
var boundOnce = logThis.bind({name: 'The first time is forever'});

Case 5:

In a callback function, apply the above rules methodically.

function outerFunction(callback) {
  callback();
}

function logThis() {
  console.log(this);
}

/*
 * Case 1: The regular old default case.
 */
 
outerFunction(logThis); // window

/*
 * Case 2: Call the callback as a method
 */
 
function callAsMethod(callback) {
  var weirdObject = {
    name: "Don't do this in real life"
  };
  
  weirdObject.callback = callback;
  weirdObject.callback();
}

callAsMethod(logThis); // `weirdObject` will get logged to the console

/*
 * Case 3: Calling the callback as a constructor. 
 */
 
function callAsConstructor(callback) {
  new callback();
}

callAsConstructor(logThis); // the new object created by logThis will be logged to the console

/*
 * Case 4: Explicitly setting `this`.
 */
 
function callAndBindToGordon(callback) {
  var boundCallback = callback.bind({name: 'Gordon'});
  boundCallback();
}

callAndBindToGordon(logThis); // {name: 'Gordon'}

// In a twist, we give `callAndBindToGordon` a function that's already been bound.
var boundOnce = logThis.bind({name: 'The first time is forever'});
callAndBindToGordon(boundOnce); // {name: 'The first time is forever'}

Conclusion:

So, this was all about “this”.

The value of ‘this’ keyword depends on the situation, analyze the situation and figure out what ‘this’ represents.

You gave your precious time to read this article.

Thank You.

How to remove links of a learndash course which has not started yet? without code changes

jQuery(document).ready(function($){
var nost=0;
if($("body").hasClass("single-sfwd-courses")){
toba='';
$("#lessons_list a.notavailable").removeAttr("href");
$("#lessons_list a.notavailable").each(function(){
toba=$(this).parent().parent().attr("class");
toba = toba.replace("lesson post-",'');
toba = toba.replace(" is_not_sample no-topics",'');
toba = toba.replace(" is_not_sample has-topics",'');
$('#learndash_topic_dots-'+toba+" a").removeAttr("href");
});
}
});

Magento 2 how to show configurable options stock availability on product page

addtocart.phtml

<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
// @codingStandardsIgnoreFile
/** @var $block \Magento\Catalog\Block\Product\View */
?>
<?php $_product = $block->getProduct(); ?>
<?php $buttonTitle = __('Add to Cart'); ?>
<?php if ($_product->isSaleable()): ?>
<div class="box-tocart">
<div class="fieldset">
<?php if ($block->shouldRenderQuantity()):
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$StockState = $objectManager->get('\Magento\CatalogInventory\Api\StockStateInterface');?>
<div class="field qty">
<label class="label" for="qty"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></label>
<div class="control">
<?php if($_product->getTypeId()=="simple") { ?>
<select class="input-text qty" name="qty" id="qty" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>">
<?php 
$availableQty= $StockState->getStockQty($_product->getId(), $_product->getStore()->getWebsiteId());
for($repli=1;$repli<=$availableQty;$repli++)
echo "<option value='$repli'>$repli</option>";
?>
</select>
<?php } else { 
$usedProducts = $_product->getTypeInstance()->getUsedProducts($_product);
$productStockd=$objectManager->get('Magento\CatalogInventory\Api\StockRegistryInterface');
$finalconfigqty=[];
foreach ($usedProducts as $child)
{$childid=$child->getId();
$productStockObj = $productStockd->getStockItem($childid);
$availableQty= round($productStockObj->getData('qty')); 
$finalconfigqty['c'.$childid]=$availableQty;
}
echo '<script>
var fichoo='.
json_encode($finalconfigqty).
'</script>';
echo '<select class="input-text qty fichoo" name="qty" id="qty"><option value=1>1</option></select>'; 
?> 
<!--input type="number" name="qty" id="qty" value="<?= $block->getProductDefaultQty() * 1 ?>" title="<?= __('Qty') ?>" class="input-text qty"
data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"
/--> <?php } ?>
</div>
</div>
<?php endif; ?>
<div class="actions">
<button type="submit"
title="<?= /* @escapeNotVerified */ $buttonTitle ?>"
class="action primary tocart"
id="product-addtocart-button">
<span><?= /* @escapeNotVerified */ $buttonTitle ?></span>
</button>
<?= $block->getChildHtml('', true) ?>
</div>
</div>
</div>
<?php endif; ?>
<?php if ($block->isRedirectToCartEnabled()) : ?>
<script type="text/x-magento-init">
{
"#product_addtocart_form": {
"Magento_Catalog/product/view/validation": {
"radioCheckboxClosest": ".nested"
}
}
}
</script>
<?php else : ?>
<script type="text/x-magento-init">
{
"#product_addtocart_form": {
"Magento_Catalog/js/validate-product": {}
}
}
</script>
<?php endif; ?>

Another important file is Magento_ConfigurableProduct/web/js/configurable.js

modified function _getSimpleProductId and added function _dofichoo

/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
/**
* @api
*/
define([
'jquery',
'underscore',
'mage/template',
'mage/translate',
'priceUtils',
'priceBox',
'jquery/ui',
'jquery/jquery.parsequery'
], function ($, _, mageTemplate, $t, priceUtils) {
'use strict';
$.widget('mage.configurable', {
options: {
superSelector: '.super-attribute-select',
selectSimpleProduct: '[name="selected_configurable_option"]',
priceHolderSelector: '.price-box',
spConfig: {},
state: {},
priceFormat: {},
optionTemplate: '<%- data.label %>' +
'<% if (typeof data.finalPrice.value !== "undefined") { %>' +
' <%- data.finalPrice.formatted %>' +
'<% } %>',
mediaGallerySelector: '[data-gallery-role=gallery-placeholder]',
mediaGalleryInitial: null,
slyOldPriceSelector: '.sly-old-price',
/**
* Defines the mechanism of how images of a gallery should be
* updated when user switches between configurations of a product.
*
* As for now value of this option can be either 'replace' or 'prepend'.
*
* @type {String}
*/
gallerySwitchStrategy: 'replace',
tierPriceTemplateSelector: '#tier-prices-template',
tierPriceBlockSelector: '[data-role="tier-price-block"]',
tierPriceTemplate: ''
},
/**
* Creates widget
* @private
*/
_create: function () {
// Initial setting of various option values
this._initializeOptions();
// Override defaults with URL query parameters and/or inputs values
this._overrideDefaults();
// Change events to check select reloads
this._setupChangeEvents();
// Fill state
this._fillState();
// Setup child and prev/next settings
this._setChildSettings();
// Setup/configure values to inputs
this._configureForValues();
$(this.element).trigger('configurable.initialized');
},
/**
* Initialize tax configuration, initial settings, and options values.
* @private
*/
_initializeOptions: function () {
var options = this.options,
gallery = $(options.mediaGallerySelector),
priceBoxOptions = $(this.options.priceHolderSelector).priceBox('option').priceConfig || null;
if (priceBoxOptions && priceBoxOptions.optionTemplate) {
options.optionTemplate = priceBoxOptions.optionTemplate;
}
if (priceBoxOptions && priceBoxOptions.priceFormat) {
options.priceFormat = priceBoxOptions.priceFormat;
}
options.optionTemplate = mageTemplate(options.optionTemplate);
options.tierPriceTemplate = $(this.options.tierPriceTemplateSelector).html();
options.settings = options.spConfig.containerId ?
$(options.spConfig.containerId).find(options.superSelector) :
$(options.superSelector);
options.values = options.spConfig.defaultValues || {};
options.parentImage = $('[data-role=base-image-container] img').attr('src');
this.inputSimpleProduct = this.element.find(options.selectSimpleProduct);
gallery.data('gallery') ?
this._onGalleryLoaded(gallery) :
gallery.on('gallery:loaded', this._onGalleryLoaded.bind(this, gallery));
},
/**
* Override default options values settings with either URL query parameters or
* initialized inputs values.
* @private
*/
_overrideDefaults: function () {
var hashIndex = window.location.href.indexOf('#');
if (hashIndex !== -1) {
this._parseQueryParams(window.location.href.substr(hashIndex + 1));
}
if (this.options.spConfig.inputsInitialized) {
this._setValuesByAttribute();
}
},
/**
* Parse query parameters from a query string and set options values based on the
* key value pairs of the parameters.
* @param {*} queryString - URL query string containing query parameters.
* @private
*/
_parseQueryParams: function (queryString) {
var queryParams = $.parseQuery({
query: queryString
});
$.each(queryParams, $.proxy(function (key, value) {
this.options.values[key] = value;
}, this));
},
/**
* Override default options values with values based on each element's attribute
* identifier.
* @private
*/
_setValuesByAttribute: function () {
this.options.values = {};
$.each(this.options.settings, $.proxy(function (index, element) {
var attributeId;
if (element.value) {
attributeId = element.id.replace(/[a-z]*/, '');
this.options.values[attributeId] = element.value;
}
}, this));
},
/**
* Set up .on('change') events for each option element to configure the option.
* @private
*/
_setupChangeEvents: function () {
$.each(this.options.settings, $.proxy(function (index, element) {
$(element).on('change', this, this._configure);
}, this));
},
_dofichoo: function(thisstock){var lsstr='';
for(var ah=1;ah<=window.fichoo['c'+thisstock];ah++) lsstr+='<option value='+ah+ ' >'+ ah + '</option>';
$('select.fichoo').html(lsstr);
},
/**
* Iterate through the option settings and set each option's element configuration,
* attribute identifier. Set the state based on the attribute identifier.
* @private
*/
_fillState: function () {
$.each(this.options.settings, $.proxy(function (index, element) {
var attributeId = element.id.replace(/[a-z]*/, '');
if (attributeId && this.options.spConfig.attributes[attributeId]) {
element.config = this.options.spConfig.attributes[attributeId];
element.attributeId = attributeId;
this.options.state[attributeId] = false;
}
}, this));
},
/**
* Set each option's child settings, and next/prev option setting. Fill (initialize)
* an option's list of selections as needed or disable an option's setting.
* @private
*/
_setChildSettings: function () {
var childSettings = [],
settings = this.options.settings,
index = settings.length,
option;
while (index--) {
option = settings[index];
if (index) {
option.disabled = true;
} else {
this._fillSelect(option);
}
_.extend(option, {
childSettings: childSettings.slice(),
prevSetting: settings[index - 1],
nextSetting: settings[index + 1]
});
childSettings.push(option);
}
},
/**
* Setup for all configurable option settings. Set the value of the option and configure
* the option, which sets its state, and initializes the option's choices, etc.
* @private
*/
_configureForValues: function () {
if (this.options.values) {
this.options.settings.each($.proxy(function (index, element) {
var attributeId = element.attributeId;
element.value = this.options.values[attributeId] || '';
this._configureElement(element);
}, this));
}
},
/**
* Event handler for configuring an option.
* @private
* @param {Object} event - Event triggered to configure an option.
*/
_configure: function (event) {
event.data._configureElement(this);
},
/**
* Configure an option, initializing it's state and enabling related options, which
* populates the related option's selection and resets child option selections.
* @private
* @param {*} element - The element associated with a configurable option.
*/
_configureElement: function (element) {
this.simpleProduct = this._getSimpleProductId(element);
if (element.value) {
this.options.state[element.config.id] = element.value;
if (element.nextSetting) {
element.nextSetting.disabled = false;
this._fillSelect(element.nextSetting);
this._resetChildren(element.nextSetting);
} else {
if (!!document.documentMode) { //eslint-disable-line
this.inputSimpleProduct.val(element.options[element.selectedIndex].config.allowedProducts[0]);
} else {
this.inputSimpleProduct.val(element.selectedOptions[0].config.allowedProducts[0]);
}
}
} else {
this._resetChildren(element);
}
this._reloadPrice();
this._displayRegularPriceBlock(this.simpleProduct);
this._displayTierPriceBlock(this.simpleProduct);
this._changeProductImage();
},
/**
* Change displayed product image according to chosen options of configurable product
*
* @private
*/
_changeProductImage: function () {
var images,
initialImages = this.options.mediaGalleryInitial,
galleryObject = $(this.options.mediaGallerySelector).data('gallery');
if (!galleryObject) {
return;
}
images = this.options.spConfig.images[this.simpleProduct];
if (images) {
if (this.options.gallerySwitchStrategy === 'prepend') {
images = images.concat(initialImages);
}
images = $.extend(true, [], images);
images = this._setImageIndex(images);
galleryObject.updateData(images);
$(this.options.mediaGallerySelector).AddFotoramaVideoEvents({
selectedOption: this.simpleProduct,
dataMergeStrategy: this.options.gallerySwitchStrategy
});
} else {
galleryObject.updateData(initialImages);
$(this.options.mediaGallerySelector).AddFotoramaVideoEvents();
}
galleryObject.first();
},
/**
* Set correct indexes for image set.
*
* @param {Array} images
* @private
*/
_setImageIndex: function (images) {
var length = images.length,
i;
for (i = 0; length > i; i++) {
images[i].i = i + 1;
}
return images;
},
/**
* For a given option element, reset all of its selectable options. Clear any selected
* index, disable the option choice, and reset the option's state if necessary.
* @private
* @param {*} element - The element associated with a configurable option.
*/
_resetChildren: function (element) {
if (element.childSettings) {
_.each(element.childSettings, function (set) {
set.selectedIndex = 0;
set.disabled = true;
});
if (element.config) {
this.options.state[element.config.id] = false;
}
}
},
/**
* Populates an option's selectable choices.
* @private
* @param {*} element - Element associated with a configurable option.
*/
_fillSelect: function (element) {
var attributeId = element.id.replace(/[a-z]*/, ''),
options = this._getAttributeOptions(attributeId),
prevConfig,
index = 1,
allowedProducts,
i,
j;
this._clearSelect(element);
element.options[0] = new Option('', '');
element.options[0].innerHTML = this.options.spConfig.chooseText;
prevConfig = false;
if (element.prevSetting) {
prevConfig = element.prevSetting.options[element.prevSetting.selectedIndex];
}
if (options) {
for (i = 0; i < options.length; i++) {
allowedProducts = [];
/* eslint-disable max-depth */
if (prevConfig) {
for (j = 0; j < options[i].products.length; j++) {
// prevConfig.config can be undefined
if (prevConfig.config &&
prevConfig.config.allowedProducts &&
prevConfig.config.allowedProducts.indexOf(options[i].products[j]) > -1) {
allowedProducts.push(options[i].products[j]);
}
}
} else {
allowedProducts = options[i].products.slice(0);
}
if (allowedProducts.length > 0) {
options[i].allowedProducts = allowedProducts;
element.options[index] = new Option(this._getOptionLabel(options[i]), options[i].id);
if (typeof options[i].price !== 'undefined') {
element.options[index].setAttribute('price', options[i].prices);
}
element.options[index].config = options[i];
index++;
}
/* eslint-enable max-depth */
}
}
},
/**
* Generate the label associated with a configurable option. This includes the option's
* label or value and the option's price.
* @private
* @param {*} option - A single choice among a group of choices for a configurable option.
* @return {String} The option label with option value and price (e.g. Black +1.99)
*/
_getOptionLabel: function (option) {
return option.label;
},
/**
* Removes an option's selections.
* @private
* @param {*} element - The element associated with a configurable option.
*/
_clearSelect: function (element) {
var i;
for (i = element.options.length - 1; i >= 0; i--) {
element.remove(i);
}
},
/**
* Retrieve the attribute options associated with a specific attribute Id.
* @private
* @param {Number} attributeId - The id of the attribute whose configurable options are sought.
* @return {Object} Object containing the attribute options.
*/
_getAttributeOptions: function (attributeId) {
if (this.options.spConfig.attributes[attributeId]) {
return this.options.spConfig.attributes[attributeId].options;
}
},
/**
* Reload the price of the configurable product incorporating the prices of all of the
* configurable product's option selections.
*/
_reloadPrice: function () {
$(this.options.priceHolderSelector).trigger('updatePrice', this._getPrices());
},
/**
* Get product various prices
* @returns {{}}
* @private
*/
_getPrices: function () {
var prices = {},
elements = _.toArray(this.options.settings),
hasProductPrice = false;
_.each(elements, function (element) {
var selected = element.options[element.selectedIndex],
config = selected && selected.config,
priceValue = {};
if (config && config.allowedProducts.length === 1 && !hasProductPrice) {
priceValue = this._calculatePrice(config);
hasProductPrice = true;
}
prices[element.attributeId] = priceValue;
}, this);
return prices;
},
/**
* Returns prices for configured products
*
* @param {*} config - Products configuration
* @returns {*}
* @private
*/
_calculatePrice: function (config) {
var displayPrices = $(this.options.priceHolderSelector).priceBox('option').prices,
newPrices = this.options.spConfig.optionPrices[_.first(config.allowedProducts)];
_.each(displayPrices, function (price, code) {
if (newPrices) {
displayPrices.amount = newPrices.amount - displayPrices.amount;
}
});
return displayPrices;
},
/**
* Returns Simple product Id
*  depending on current selected option.
*
* @private
* @param {HTMLElement} element
* @returns {String|undefined}
*/
_getSimpleProductId: function (element) {
// TODO: Rewrite algorithm. It should return ID of
//        simple product based on selected options.
var allOptions = element.config.options,
value = element.value,
config;
config = _.filter(allOptions, function (option) {
return option.id === value;
});
config = _.first(config);
if(! _.isEmpty(config)) this._dofichoo(_.first(config.allowedProducts));
return _.isEmpty(config) ?
undefined :
_.first(config.allowedProducts);
},
/**
* Show or hide regular price block
*
* @param {*} optionId
* @private
*/
_displayRegularPriceBlock: function (optionId) {
if (typeof optionId != 'undefined' &&
this.options.spConfig.optionPrices[optionId].oldPrice.amount != //eslint-disable-line eqeqeq
this.options.spConfig.optionPrices[optionId].finalPrice.amount
) {
$(this.options.slyOldPriceSelector).show();
} else {
$(this.options.slyOldPriceSelector).hide();
}
},
/**
* Callback which fired after gallery gets initialized.
*
* @param {HTMLElement} element - DOM element associated with gallery.
*/
_onGalleryLoaded: function (element) {
var galleryObject = element.data('gallery');
this.options.mediaGalleryInitial = galleryObject.returnCurrentImages();
},
/**
* Show or hide tier price block
*
* @param {*} optionId
* @private
*/
_displayTierPriceBlock: function (optionId) {
var options, tierPriceHtml;
if (typeof optionId != 'undefined' &&
this.options.spConfig.optionPrices[optionId].tierPrices != [] // eslint-disable-line eqeqeq
) {
options = this.options.spConfig.optionPrices[optionId];
if (this.options.tierPriceTemplate) {
tierPriceHtml = mageTemplate(this.options.tierPriceTemplate, {
'tierPrices': options.tierPrices,
'$t': $t,
'currencyFormat': this.options.spConfig.currencyFormat,
'priceUtils': priceUtils
});
$(this.options.tierPriceBlockSelector).html(tierPriceHtml).show();
}
} else {
$(this.options.tierPriceBlockSelector).hide();
}
}
});
return $.mage.configurable;
});

How to manage Magento 2 product attribute values options using console

This is an update to my previous script which was used to add custom product attribute values using backend –
Add values to product attribute

Read the previous^ part to get introduction about how it works. you will have to use browser console (ctrl + shift +j in Google chrome)

First of all initialize the array mimim with the final set of values you want to see in backend. The script then automatically removes the extra options first(values which are not in mimim but in present in backend), then it adds the remaining values from mimim (which are in mimim but not at backend)

$jq=new jQuery.noConflict();
var mimim=["Abalone",
"Titanium Druzy",
"Tourmaline",
"Turquoise",
"Yellow Zircon",
"Light Blue Druzy"];
var trans=[];var o=0;
$jq('#manage-options-panel tbody tr td:nth-child(3) input').each(
function(a,b){
//if(a>10)return false;
//alert(b.value);
trans.push(b.value);
if($jq.inArray(b.value,mimim)==-1){ o++;$jq('#delete_button_'+b.name.slice(14,-4)).click(); trans.pop();}
});
alert(o+" items removed");
$jq.each(mimim,function(a,b){
//console.log($jq('#manage-options-panel tbody tr td:nth-child(3) input').val());
if($jq.inArray(b,trans)==-1){
$jq('#add_new_option_button').click();
$jq('#manage-options-panel tbody tr:last-child td:nth-child(3) input').val(b);
}
});

Magento 2.1x – Add options in custom product attribute using backend

Go to the attribute edit page using magento 2 admin panel (Stores > Attributes > Product > Edit attribute)
Use console – ctrl + shift + J
Edit the js array mimim and then paste the following code in console.

$jq=new jQuery.noConflict();
var mimim=["Black","Blue","Blue Black"];
$jq.each(mimim,function(a,b){
$jq("#add_new_option_button").click();
$jq("#manage-options-panel tbody tr:last-child td:nth-child(3) input").val(b);
});

How to unlike all Facebook pages from your profile and Clean your newsfeed

Hello humans, I have tried to keep this tutorial simple and detailed. Please proceed with caution.

The following tutorial and the procedure will unlike pages you have liked from your profile, Please be clear about that the fact that the code provided here is clean and does not intend to do any harm to your Facebook account.

Note – This code uses jQuery library in its original form. So you do not need to be scared while using this “scary” looking code.

Continue reading “How to unlike all Facebook pages from your profile and Clean your newsfeed”