Background info: I'm using WooCommerce and Gravity Forms, and trying to make it so the Add to Cart button is inactive according to two conditions - either there are no attendees registered, or the date hasn't been selected from the product variation dropdown. The user should only be able to move forward if both sections are completed.
The Gravity Forms component of this has a popup module to sign up those attendees, but the summary is displayed outside the module and on the main product page. The class .gpnf-no-entries lives on the "outside" of the Gravity Forms module, since it's always visible on the page. .gpnf-nested-entries and .gpnf-row-actions are also outside the module, but rely on information from within the module. .tingle-btn is a class used on multiple buttons inside the module - to add an attendee, cancel editing, or delete that attendee (unsure if I need a loop on here - alerts were working without one, and it seems like there's something else causing issues regardless).
Issues: It was working at one point, but only after the second click (anywhere on the page). There's also a second issue - on this form, if you've added an attendee but not added the product to the cart, the page retains any info you've put in. So what happens is, if you refresh the page and have old attendee info already there, the Add to Cart never gets clickable after selecting a date, even though both areas are filled out.
Screenshots:
I'm still somewhat of a beginner here so it's quite possibly something silly.
<script>
var modalButtons = document.querySelectorAll('.tingle-btn');
var noEntries = document.querySelector('.gform_body .gpnf-no-entries');
var entryField = document.querySelectorAll(".gpnf-nested-entries .entry-field[style='display: block;']");
var nestedEntriesDelete = document.querySelector('.gpnf-row-actions .delete');
var addToCart = document.querySelector('.single_add_to_cart_button');
var wcVariation = document.querySelector('.woocommerce-variation-add-to-cart');
var selectCheck = document.querySelector('#select-date-option');
//When date selection dropdown is changed, check value and check for "no entries" message
document.addEventListener('change', function (event) {
if (!event.target.matches('selectCheck')) {
if ((noEntries.style.display !== 'none') || (selectCheck.value === '')) {
addToCart.classList.add('disabled');
wcVariation.classList.remove('woocommerce-variation-add-to-cart-enabled');
wcVariation.classList.add('woocommerce-variation-add-to-cart-disabled');
}
else {
addToCart.classList.remove('disabled');
wcVariation.classList.add('woocommerce-variation-add-to-cart-enabled');
wcVariation.classList.remove('woocommerce-variation-add-to-cart-disabled');
}
}
}, false);
// When attendee is deleted, check to see if there are any entry fields left
document.addEventListener('click', function (event) {
if (!event.target.matches('nestedEntriesDelete')) {
if (entryField.length <= 3) {
addToCart.classList.add('disabled');
wcVariation.classList.remove('woocommerce-variation-add-to-cart-enabled');
wcVariation.classList.add('woocommerce-variation-add-to-cart-disabled');
}
}
}, false);
// Check for "no entries" and no date selection value when buttons to add or remove attendees are clicked
document.addEventListener('click', function (event) {
if (!event.target.matches('modalButtons')) {
if ((noEntries.style.display !== 'none') || (selectCheck.value === '')) {
addToCart.classList.add('disabled');
wcVariation.classList.remove('woocommerce-variation-add-to-cart-enabled');
wcVariation.classList.add('woocommerce-variation-add-to-cart-disabled');
}
else {
addToCart.classList.remove('disabled');
wcVariation.classList.add('woocommerce-variation-add-to-cart-enabled');
wcVariation.classList.remove('woocommerce-variation-add-to-cart-disabled');
}
}
}, false);
</script>
I ended up doing this a much simpler way by adding classes:
<script>
var noEntries = document.querySelector('.gform_body .gpnf-no-entries');
var entriesContainer = document.querySelector('.gpnf-nested-entries-container');
var addToCart = document.querySelector('.single_add_to_cart_button');
//When page is fully loaded, check for cached entries
window.addEventListener('load', function () {
//if there are entries, show the add to cart button
if (noEntries.style.display === 'none'){
entriesContainer.classList.add('has-entries');
addToCart.classList.add('do-add');
addToCart.classList.remove('dont-add');
}
//if there are no entries, disable the add to cart button
else if (noEntries.style.display === ''){
entriesContainer.classList.remove('has-entries');
addToCart.classList.add('dont-add');
addToCart.classList.remove('do-add');
}
//if the form isn't present, don't do any of this
else if (noEntries = 'null'){
//do nothing
}
});
//When the container with the form and the entries is clicked, check for entries
document.addEventListener('click', function (event) {
if (!event.target.matches('#gform_wrapper_41')) {
setInterval(function() {
//if an entry is added, show the add to cart button
if (noEntries.style.display === 'none'){
entriesContainer.classList.add('has-entries');
addToCart.classList.add('do-add');
addToCart.classList.remove('dont-add');
}
//if all entries are removed, disable the add to cart button
else if (noEntries.style.display === ''){
entriesContainer.classList.remove('has-entries');
addToCart.classList.add('dont-add');
addToCart.classList.remove('do-add');
}
},2000);
}
}, false);
</script>
Related
I am currently trying to synchronize two checkboxes on a page.
I need the checkboxes to be synchronized - to this end, I'm using a Tampermonkey userscript to pick up when one of them is clicked. However, I'm at a loss as to how to do it.
I believe they are not actually checkboxes, but ExtJS buttons that resemble checkboxes. I can't check whether they're checked with JQuery because of this: the checked value is appended to a class once the JS behind the button has run.
I have tried preventDefault and stopPropagation, but either I'm using it wrong or not understanding its' usage.
I'm not quite clever enough to just call the JS behind the box instead of an onclick event. Otherwise, that would solve my issue.
This is my code:
//Variables - "inputEl" is the actual button.
var srcFFR = "checkbox-1097";
var destFFR = "checkbox-1134";
var srcFFRb = "checkbox-1097-inputEl";
var destFFRb = "checkbox-1134-inputEl";
//This checks if they're synchronised on page load and syncs them with no user intervention.
var srcChk = document.getElementById(srcFFR).classList.contains('x-form-cb-checked');
var destChk = document.getElementById(destFFR).classList.contains('x-form-cb-checked');
if (srcChk == true || destChk == false) {
document.getElementById(destFFRb).click();
} else if (destChk == true || srcChk == false) {
document.getElementById(srcFFRb).click();
}
//This is where it listens for the click and attempts to synchronize the buttons.
$(document.getElementById(srcFFRb)).on('click', function(e) {
e.preventDefault();
if (document.getElementById(srcFFR).classList == document.getElementById(destFFR).classList) {
return false;
} else {
document.getElementById(destFFRb).click();
}
});
$(document.getElementById(destFFRb)).on('click', function(e) {
e.preventDefault();
if (document.getElementById(srcFFR).classList == document.getElementById(destFFR).classList) {
return false;
} else {
document.getElementById(srcFFRb).click();
}
});
I'm at a bit of a loss...any help would be greatly appreciated.
Figured it out - I was comparing class lists without singling out what I wanted to actually match.
My solution:
$(document.getElementById(srcFFRb)).on('click', function(){
if (document.getElementById(srcFFR).classList.contains('x-form-cb-checked')
== document.getElementById(destFFR).classList.contains('x-form-cb-checked')) {
return false;}
else {
document.getElementById(destFFRb).click();;
}});
$(document.getElementById(destFFRb)).on('click', function(){
if (document.getElementById(srcFFR).classList.contains('x-form-cb-checked')
== document.getElementById(destFFR).classList.contains('x-form-cb-checked')) {
return false;}
else {
document.getElementById(srcFFRb).click();;
}});
So I added rule on button in record subgrid, so when condition is met it will return false and supposedly hide the button. The script is working but instead of hiding the button, it make the button disabled (can't be clicked). Am I missing something? Never used xrm ribbon workbench before.
Here is my script below:
function validatebuttondelete()
{
var entityName = Xrm.Page.data.entity.getEntityName();
if(entityName == "msdyn_workorder")
{
if(Xrm.Page.ui.getFormType() != 1)
{
var received = Xrm.Page.getAttribute("mjt_received").getValue()
var receivedSp = Xrm.Page.getAttribute("mjt_received_sp").getValue()
var stageName = Xrm.Page.data.process.getActiveStage().getName();
if(stageName == "Branch")
{
if(received == 0)
{
return false;
}
else
{
return true;
}
}
if(stageName == "Service Point")
{
if(receivedSp == 0)
{
return false;
}
else
{
return true;
}
}
}
}
}
You can read about Enable rule & Display rule here.
To hide the delete button (trash can/dustbin icon) in subgrid completely - you can simply right click & “Hide” it. Read more
But your requirement is little more complex. Has to be hidden based on some rule. Atleast for you it was disabled. Many people tried that & finally ended up in simple alert message as the button didn’t cooperate.
Reference
the (missing: Enable rule) did not hide the delete button, but allowed us to prevent the Delete action for the disabled records. We went ahead and unhid the Delete button, and then selected the Customize Command option. This populated the Mscrm.DeleteSelectedRecord command under the Command in Ribbon Workbench. We added another Enable rule called RestrictDeleteFromSubgrid
Background: I have an external device (barcode reader) that sends information back to a tablet when the user scans something. I subscribe to that channel and I need the value to be inside the currently focused cell and write it there.
Bug: I can catch the subscription and write the value visually in the Input box, but it never reaches the JSON underneath.
I also tried $scope.$apply() but it did not change anything (maybe I used it wrong).
"Working" Plunker with the problem
$scope.randomClickOnStuff = function() {
// Here Randomely publish stuff with value so we can write it in specific field.
window.setTimeout(function() {
if (!$scope.stopMe) {
vm.objectOtSubscribeTo.publish(channelToUse, Date.now());
$scope.randomClickOnStuff();
} else {
// Stop the loop.
}
}, 1000);
};
var callbackCompleted = function(resultValue) {
// Important code Here
// Code to write in the input box here.
console.log(resultValue);
if (document.activeElement.localName == "input") {
// Option 1:
//--> Work Visually <-- but do not put the value inside the JSON.
document.activeElement.value = resultValue;
$scope.$apply();
// Option 2:
// http://stackoverflow.com/questions/11873627/angularjs-ng-model-binding-not-updating-when-changed-with-jquery
// Problem: The "document.activeElement.attributes['ng-model'].value" is not link with the scope, but with the ng-repeat row. So I have access to the Scope, but not the Row item.
//var binding = document.activeElement.attributes['ng-model'].value;
// Rule: I might not know where the Item is so I cannot do $scope.complexObject[row][binding]
} else {
console.log("not inside a Input box.");
}
};
vm.objectOtSubscribeTo.subscribe(channelToUse, callbackCompleted);
Thanks
One solution would be to keep track of the selected row and cell by setting them on focus of one of the cells
$scope.focusedRow = false;
$scope.focusedCell = false;
$scope.setFocused = (row, cell) => {
$scope.focusedRow = row;
$scope.focusedCell = cell;
};
/* In callback... */
if ($scope.focusedRow !== false && $scope.focusedCell !== false) {
$scope.$apply(
() => $scope.complexObject[$scope.focusedRow]
["cellInTheRow"][$scope.focusedCell] = resultValue
);
}
<input type="text" ng-model="row.cellInTheRow[key]"
ng-focus="setFocused(rowKey, key)" ng-blur="setFocused(false, false)">
Example: https://plnkr.co/edit/och5PoepJuRde0oONIjm?p=preview
I have a div which contains an input element to enter some values. These values are added just above the div as a list element upon pressing enter or onFocusOut event. To this point it is fine. But if user types some value and does not press enter and directly clicks on save button, the onFocusOut function for that div should not be called. Instead it should take that typed value and call some save function. Do you have any suggestion on how to detect it?
My code snippet is here
JS:
divInput.onkeypress = function (event){
return someTestFunc();
}
divInput.tabIndex="-1";
$(divInput).focusout(function (e) {
if ($(this).find(e.relatedTarget).length == 0) {
addToList();
}
});
It is not a very delicate solution, but you could use a setTimeout before adding the item to the list and clear the setTimeout on save.button click.
Try this:
var $saveButton = $('#exampleButton')[0],
$divInput = $('#exampleInput')[0],
timedEvent = -1;
$($saveButton).on('click', function(event){
if(timedEvent) {
clearTimeout(timedEvent)
}
alert('not add to list & save');
})
$divInput.tabIndex="-1";
$($divInput).on('focusout', function(e) {
timedEvent = window.setTimeout(function() {
if ($(this).find(e.relatedTarget).length == 0) {
alert('add to list');
}
}, 200);
});
Check this working fiddle
I'm writing js for a status update system to be used on various pages throughout a app that I'm working. I am really just starting to get more comfortable with javascript so it has been somewhat of a challenge to get to the point where I have everything now.
The status system is basically a facebook clone. For the most part everything is supposed to function the way that facebook's status updates and status comments do. The intended behavior is that when the user clicks in the status textarea, the div under the status textarea slides out revealing the submit button as well as some other checkboxes.
If the user clicks anywhere else on the page except a link or any element that has the class prevent_slideup the div slides up hiding the submit button and any checkboxes.
I'm using a document.body click function to determine what the user clicked on so I know which form elements to hide if I should even hide them. I do not want this slideup to take place on a textarea if that textarea has focus or the user is selecting a checkbox that goes with that form. Hence the prevent_slideup class. I also do not want to bother running the slideup logic if the user has clicked on a link. I'd prefer they just leave the page without having to wait for the animation.
The code that I was using to accomplish this task can be found in the $(document.body).click(function (e) section below where I'm doing a .is('a') check on the event target.
This code works as expected in chrome and firefox, however in ie when a link is clicked for the first time it seems that the element stored in var target is actually a div instead of an anchor. What ends up happening is that the submit div slides up and the user is not taken to the link that they just clicked on. If a link is clicked a second time the user is taken to the page as you would expect.
It seems to me that there's some kind of a lag in ie as to what the current event being fired is.
The entire status module is working other than this one strange ie bug regarding the users click on the link not being carried out the first time that they click a link after opening the status textarea. Does anything jump out in this script that would explain this behavior or does anyone have any other advice?
Thanks in advance for your help.
$(document).ready(function(){
$("textarea.autoresize").autoResize();
});
$(document.body).click(function (e){
var target = e.target || e.srcElement;
console.log(target);
console.log($(target).is('a'));
if($(target).hasClass('prevent_slideup') || $(target).is('a'))
{
return true;
}
else
{
var active_element = document.activeElement;
var active_status_id = $(active_element).attr('data-status_id');
var active_has_data_status_id = (typeof active_status_id !== 'undefined' && active_status_id !== false) ? true : false;
$('textarea').each(function(){
if($(this).hasClass('status_comment_textarea'))
{
var status_id = $(this).attr('data-status_id');
if($('#comment_textarea_'+status_id).val() === '' && (!active_has_data_status_id || active_status_id !== status_id))
{
hide_status_comment_submit(status_id);
}
}
else if($(this).attr('id') === 'status_textarea')
{
if($('#status_textarea').val() === '' && $(active_element).attr('id') !== 'status_textarea')
{
$('#status_textarea').html($("#status_textarea").attr('placeholder'));
hide_status_submit();
}
}
});
return true;
}
});
$("#status_textarea").live('click', function(){
if($('#status_textarea').val() === $("#status_textarea").attr('placeholder'))
{
$('#status_textarea').html('');
}
show_status_submit();
return false;
});
$(".comment_toggle").live('click', function(){
var status_id = $(this).attr('data-status_id');
show_status_comment_submit(status_id);
return false;
});
$(".status_comment_submit").live('click', function(){
var status_id = $(this).attr('data-status_id');
$('#status_comment_submit_wrapper_'+status_id).addClass('status_comment_submit_successful');
return false;
});
$(".show_hidden_comments").live('click', function(){
var status_id = $(this).attr('data-status_id');
$('#status_hidden_comments_'+status_id).show();
$(this).hide();
return false;
});
function hide_status_submit()
{
$("#status_textarea").removeAttr('style');
$("#status_textarea").blur();
$("#status_block").removeClass('padding_b10');
$("#status_submit_wrapper").slideUp("fast");
return false;
}
function show_status_submit()
{
if ($("#status_submit_wrapper").is(":hidden"))
{
$("#status_block").addClass('padding_b10');
$("#status_submit_wrapper").slideDown('fast');
}
return false;
}
function hide_status_comment_submit(status_id)
{
if(!$('#status_comment_submit_wrapper_'+status_id).is(":hidden"))
{
$('#status_comment_submit_wrapper_'+status_id).hide();
$('#fake_comment_input_'+status_id).show();
$('#comment_textarea_'+status_id).removeAttr('style');
}
return false;
}
function show_status_comment_submit(status_id)
{
if($('#status_comment_submit_wrapper_'+status_id).is(":hidden"))
{
$('#fake_comment_input_'+status_id).hide();
$('#status_comment_submit_wrapper_'+status_id).show();
$('#comment_textarea_'+status_id).focus();
}
return false;
}
function status_comment_submit_successful()
{
hide_status_comment_submit($('.status_comment_submit_successful').attr('data-status_id'));
$('.status_comment_submit_successful').removeClass('status_comment_submit_successful');
return false;
}
I figured out that there were two main issues with my script...
1.) The document.body function and the #status_textarea live click funtioins were conflicting with each other.
2.) After adding the logic for the #status_textarea function into the document.body function I noticed that the script still didn't quite work as expected in internet explorer unless I had an alert in the function. The problem at this point was that the autoresize plugin that I'm using on the textarea was also conflicting with the document.body function.
I was able to rectify the situation by adding a dummy text input and hiding the status textarea. On click of the dummy text input the status textarea is shown and the the dummy text input is hidden. I have no idea why this worked, but it seems to have solved my problems.