Given I have a model Post which can have up to n Comments (the number is controlled by the Backend) and I have a view which allows to add a Comment via a AJAX request. What is the best way to tell the view upon the nth request to disable the Add-Comment-Form?
The nth request is successfull so status code should still be 200/201 but the backend already "knows" that the nth + 1 call will invalidate the Post, so i want to somehow tell this to the view so it can take action before the user experiences the (catched) error upon nth + 1 submit.
By now the backend renders html which is then simply attached to a div in the DOM, with JSON I might add an additional field but then would move the templating to the view again.
If someone has an idea for an elegant solution?
Try having your server render javascript value for comment count and max comments. Then you can increment the count value in your success function as well as perhaps render the html comment.
Something like
var commentCount = *value from server*;
var maxComments =*value from server*;
$('#mybutton').click(function(){
$.ajax({
// your code here
})
. success(function (response) {
// process response
commentCount ++;
if( commentCount >= maxComments)
$('#mybutton). prop('disabled', true);
});
Just hide the "Add comment" button by JavaScript, when it will be "nth + 1". Or remove eventListener from button and change caption to smth like "You reached max comments".
Keep a track of the number of clicks. After the n th click, you can change the disabled attribute of your button to true.
$(".myButton").attr("disabled",true);
Related
I have a simple page, which in URL https://www.myshop.com/user/quickact, and the html looks like :
<input id="pid" />Keyin Product ID
<input id="count" />Keyin product count
<a id="addList" href="javascript:void(0);" class="buttonAction">Add to List</a>
<!--some external js file handle click event for this link-->
When user clicked hyper text [Add to List], my page will :
Get pid/count value, put into a javascript array.
Reset pid with '', reset count with 1'.
Things I'm now doing in GTM are :
When PageURL contains "user/quickact"
And user clicked link with ID "addList"
Get pid/count value, and send them into GA via GTM tag.
Here's how I read them in GTM's custom variable
function()
{
var pid=document.getElementById('pid').value;
var count=document.getElementById('count').value;
return pid+','+count;
}
My problem is, GA event did fired, but the value...my custom variable always return ',1'.
It means my GTM tag fired after page's action, so it can only read reset value, not the actual value user key-in.
Can any one gives suggestion to solve this ?
I'm not sure if there's a solution to control the order of execution of multiple events, but one workaround would be to provide the required data for GTM with the same functionality, that you are using to get the values and to reset them. This assumes, that you have control over this code.
You could do something like this:
function yourFunctionForButtonClick() {
//Your code to get pid/count value, put into a javascript array.
//new code to provide data to GTM
dataLayer.push({
event: 'newPidProvided',
pidcountvalue: pid + ',' + count
});
//Your code to reset pid with '', reset count with 1'.
}
In this case, you need to set up your GTM trigger to listen to newPidProvided event instead of clicks, while still checking for "user/quickact" to be present in the URL. (Which could also be done in the function, if no other tag or trigger uses pid+count in GTM on other pages.) You'll also have to use a dataLayer variable instead of custom JavaScript type.
My page fires off an ajax query, where the MySQL Db is queried and the results are returned. (all successful).
Those results are formatted for output as a shopping gallery/catalogue and also as an accordion filter menu. So I can filter the shopping catalogue display. eg say I want to see only items that are red.
All is working so far.
My problem is with the filter accordion menu - dynamically created in js.
When I click on any selectable item in the tab-content, nothing happens. This means the parameter that should be sent, isn't being sent.
If I hard code the accordion filter or even load it with my server-side language, into the html directly, the filtering does send off the parameter and so the shopping catalogue is adjusted accordingly but, in that scenario, I am unable to dynamically change the filter menu.
I think the code I shall post below is the relevant code that recognises changes in the originally loaded content and fires off the ajax but (I think) it doesn't understand any changes to textboxes in the dynamically loaded content.
Please help me to understand what I need to add that will make dynamically loaded content fire-off to the ajax calls.
var $checkboxes = $("input:checkbox");
function update_nav_filter(opts) {
$.ajax({
type: "POST",
url: "/php-queries/product-filter-query.php",
dataType: 'json',
cache: false,
data: {
filterOpts: opts
},
success: function(records) {
//console.log(records);
//alert('SUCCESS!');
// alert(records);
$('#filters_div').html(makeFilter(records));
}
});
}
$checkboxes.on("change", function() {
//alert('there is a change is checkbox status'); // working on page load but not when any checkbox is clicked-on
var opts = getCatalogueFilterOptions();
updateCatalogue(opts);
update_nav_filter(opts);
});
$checkboxes.trigger("change");
Any help greatly appreciated.
I have created an event listener.
Following page-load, I select an item in the JS generated nav filter. eg pedal_bins in the sub_category section. I am then shown a display of pedal_bins. :)
Then I select 'kettles', another sub_category but I can only see the last sub_category that I click on. The pedal_bins disappear.
How best can I build and remove items with a single click? Store in a session parameter and then
a. remove the latest click if it matches whats in the session
b. add the latest click if its not already in the session
Then submit whatever the array is at that stage?
Or, is there a better way to run this?
Here's the listeneer
enter code here
document.getElementById("filtering_div").addEventListener("click",function(e) {
// e.target was the clicked element
if (e.target && e.target.matches("input")) {
var parameter = e.target.id;
//console.log("Anchor element", parameter , " was clicked" );
var opts = getCatalogueFilterOptions(parameter);
console.log(opts);
// update_nav_filter(opts);
updateCatalogue(opts);
}
});
You have a "delegation" problem. When you create a dynamic element, in order to be able to act on the newly created element, you have to reference it as a child element that was originally loaded with the DOM.
For example, if you have an element called <div id="top"></div> and you create a dynamic element, let's say <button id="test">Click</button> in there, you'll have to refer to that div when adding an event listener.
$("#top").on('click', '#test', function(){
//event related code goes here.
});
Here is a fiddle I created that explains the whole thing with some examples.
If you have any questions about it, please let me know.
I am trying to replace some text in an input field using JS but the view model overrides my commands each time. This is the HTML I start with:
<td class="new-variants-table__cell" define="{ editVariantPrice: new Shopify.EditVariantPrice(this) }" context="editVariantPrice" style="height: auto;">
<input type="hidden" name="product[variants][][price]" id="product_variants__price" value="25.00" bind="price" data-dirty-trigger="true">
<input class="mock-edit-on-hover tr js-no-dirty js-variant-price variant-table-input--numeric" bind-event-focus="onFocus(this)" bind-event-blur="onBlur(this)" bind-event-input="onInput(this)">
</td>
I run this JS:
jQuery('#product_variants__price').siblings().removeAttr('bind-event-focus');
jQuery('#product_variants__price').siblings().removeAttr('bind-event-input');
jQuery('#product_variants__price').siblings().removeAttr('bind-event-blur');
jQuery('#product_variants__price').siblings().focus()
jQuery('#product_variants__price').siblings().val("34.00");
jQuery('#product_variants__price').val("34.00");
And I'm left with the following HTML:
<td class="new-variants-table__cell" define="{ editVariantPrice: new Shopify.EditVariantPrice(this) }" context="editVariantPrice" style="height: auto;">
<input type="hidden" name="product[variants][][price]" id="product_variants__price" value="34.00" bind="price" data-dirty-trigger="true">
<input class="mock-edit-on-hover tr js-no-dirty js-variant-price variant-table-input--numeric">
</td>
The problem is that each time I click the input field the value is reverted to what it was when the page loaded.
I've also tried running the command in the parent td along with my value change, to simulate the editing of a variant and preventing default with no success:
jQuery('#product_variants__price').siblings().bind('input', function (e) {
e.preventDefault();
return false;
});
jQuery('#product_variants__price').siblings().bind('focus', function (e) {
e.preventDefault();
return false;
});
jQuery('#product_variants__price').siblings().focus()
jQuery('#product_variants__price').siblings().val("£34.00");
jQuery('#product_variants__price').val("£34.00");
jQuery('#product_variants__price').siblings().keydown()
Parent td function:
new Shopify.EditVariantPrice(jQuery('#product_variants__price').parent())
So how can I successfully edit this value in the inputs and also update the Shopify view model?
You can try this for yourself by going here:
https://jebus333.myshopify.com/admin/products/2521183043
login jebus333#mailinator.com
password shop1
EDIT: I've tried to find the view model on the page but with no success. Plus, there are no network calls when editing the values in the input fields, leading me to believe the values are being pulled back from somewhere on page.
Try this:
var old = Shopify.EditVariantPrice.prototype.onFocus;
Shopify.EditVariantPrice.prototype.onFocus = function(t) {
this.price = '50.00'; // Use the price you want here
old.call(this, t);
};
jQuery('#product_variants__price').siblings().triggerHandler("focus");
jQuery('#product_variants__price').siblings().triggerHandler("blur");
If it works for you, it's possible that the following will be sufficient:
Shopify.EditVariantPrice.prototype.onFocus = function(t) {
this.price = '50.00'; // Use the price you want here
};
Well, there is a kind of a dirty solution...
First of all you'll need a sendkeys plugin. In fact that means you'll need to include this and this JS libraries (you can just copy-paste them in the console to test). If you don't want to use the first library (I personally find it quite big for such a small thing) you can extract only the key things out of it and use only them.
The next step is creating the function which is going to act like a real user:
function input(field, desiredValue) {
// get the currency symbol while value is still pristine
var currency = field.val()[0];
// move focus to the input
field.click().focus();
// remove all symbols from the input. I took 10, but of course you can use value.length instead
for (var i = 0; i < 10; i++) field.sendkeys("{backspace}");
// send the currency key
field.sendkeys(currency);
// send the desired value symbol-by-symbol
for (var i = 0; i < desiredValue.length; i++) field.sendkeys(desiredValue[i]);
}
Then you can simply call it with the value you wish to assign:
input($("#product_variants__price").next(), "123.00");
I did not really manage to fake the blur event because of lack of the time; that is why I was forced to read the currency and pass .00 as a string. Anyway you already have a way to go and a quite working solution.
Looks like you're trying to automate editing of variant prices of products in Shopify's admin panel.
Instead of playing around with the DOM of Shopify's admin page, I'll suggest using Shopify's bulk product editor which lets you set prices of all variants in a single screen. I feel that you'll have better luck setting the variant prices using JavaScript on the bulk product editor page.
Clicking on the 'Edit Products' button as shown in the screenshot below will open the bulk product editor.
Also check if browser based macro recording plugins like iMacro can be of your help (you can also code macros with JS in iMacro).
Ok here is my question
I have MySQL with the following order:
ids - radio - link - time - artist - title - disliked
ids is the ID of the Media
from page LISTEN.php I have random selection of video from the Database.
I have already created in LISTED.php to SHOW THE ID of the Video also the Artist and Title.
**I need to have a button on LISTEN.php where when somebody clicks it for example the name of the button - > Dislike
so if someone clicks it AJAX or SOMEHOW not to refresh the page but in the same time when the DISLIKE button is clicked to Update in the MySQL (once again without refreshing the page) to Disliked - 1, (when this button is pressed again for the same video to update to 2 / 3 / 4 and so on. Right now all of my videos are 0.
I need to be able to View not LIKED videos, for example
select * from RANDOM where DISLIKED is Higher number then >0**
I am not a very good in PHP so please help me, one more time the PAGE SHOULD NOT REFRESH.
Your help would be greatly appreciated.
Ajax in jQuery works like this:
var myData=1;
$.ajax({
type:'POST',//type of ajax
url:'mypage.php',//where the request is going
data:myData,//the variable you want to send
beforeSend:function(xhr){//as a standard, I add this to validate stuff
if(someThingWrong===true)xhr.abort//aborts xhttpRequest
},
success:function(result){
//result is your result from the xhttpRequest.
}
});
This will not refresh your page but send a 'POST' to the url specified. On your specified page you want to do whatever it is you want to do and say return a result. In my example I'll do something simple:
if($_POST['myData']===1)return True;
That's the basics of an AJAX request using jQuery.
EDIT!
initiating an AJAX script:
I'm only guess as I don't know your elements within your html nor your scripts what so ever! So you'll have to make adjustments!
$('button.dislike').click(function(){
$.ajax({
type:'POST',
url:'disliked.php',
data:{dislike:$(this).attr('id')},
success:function(result){
$(this).prev('span').append(result);
}
});
});
PHP:
don't use mysql, it's now been depreciated and is considered bad practise, I also don't know why're using sprintf on the query? :S
$DBH=new mysqli('location','username','password','database');
$get=$DBH->prepare("SELECT dislike FROM random WHERE ids=?");
$get->bind_param('i',$_POST['dislike']);
$get->execute();
$get->bind_result($count);
$get->close();
$update=$DBH->prepare('UPDATE random SET dislike=? WHERE ids=?');
$update->bind_param('ii',++$count,$_POST['dislike']);//if you get an error here, reverse the operator to $count++.
$update->execute();
$update->close();
return String $count++;
This will only work if there in your HTML there is a series of buttons with ID's matching those in your database. So
$get=$DBH->prepare('SELECT ids FROM random');
$get->execute();
$get->bind_result($ids);
while($get->fetch()){
echo"<button class='dislike' id='".$ids."'>Dislike this?</button>";
}
Hope you get the general idea of how I'm managing your dislike button system XD lol
I'm currently working on a Qualtrics survey in which respondents have to solve a long list of anagrams, and then answer some demographic questions.
To make the anagram part easier, I've used a Loop and Merge block: the first field is the anagram to be solved, the second field is the solution of the anagram, and the survey can therefore check the answer of the respondent against the solution for each anagram.
As it is, the survey is working perfectly: however, I'd like to allow respondents to prematurely exit the loop by typing "EXIT" in the response field, and to redirect them to the next question block (the demographic questions).
This is typically something that is achieved using "Skip" logic: however, skipping to the end of the block does not do the trick (the loop restarts). I managed to redirect them to the end of the survey, but not to the demographic question block.
Is there a way to use javascript to jump to the demographic block or exit the loop and merge block prematurely? Am I missing a Qualtrics option that would do the trick?
If this is still relevant to you: I needed the same functionality and this is how I solved it: First, I define a helper variable, call it EndLoop, which I initialize to 0. Then I set up a function to change the value of EndLoop to 1 after people hit a button, additionally I add a display logic to the question in the loop showing them only if EndLoop is still 0 and hiding the questions as soon as EndLoop is 1.
This is a step-by-step instruction and the javascript and html code.
The bold stuff is what you need to do, the bulletpoints a more detailed instruction how to do it.
1. Before your loop-and-merge define an embedded data field called EndLoop and initialize it as 0.
Go to the Survey Flow Panel
Add a new element > select embedded data field
Name the field 'EndLoop'
Set its value t0 the number 0 by click on the link "set value now"
Make sure you move it before the merge-and-loop block
2. For each item in the loop set a display logic to show them conditional on 'EndLoop' = 0
Go to the options menu of each question in the loop
Select "add display logic"
Select "Embedded Data" from the first dropdown menu
A new type field opens > as name type EndLoop + select "is equal to" + type 0 as value
3. Insert a customized button into the page where people should be able to opt-out. The button runs a user-defined function called setEndLoop() onclick.
Click on the question where the button should appear
On the top-right of the question text select "html view"
The code I used is:
<input id="css-class-mybutton" onclick="setEndLoop()" value=" done " type="button">
If you want to change the button text, change the " done " in value = " done "
4. Define the function setEndLoop() using custom javascript to change the value of EndLoop to 1 and emulate a next button click
Go to the options menu of each question in the loop
Select "add JavaScript"
The code I used is:
/* Get the EndLoop variable */
var EndLoop = "${e://Field/EndLoop}";
Qualtrics.SurveyEngine.addOnload(function(){
/* hide previous and next button */
$('NextButton') && $('NextButton').hide();
$('PreviousButton') && $('PreviousButton').hide();
/* Function: on click on user-defined button -> change the field EndLoop */
var that = this;
setEndLoop = function(){
Qualtrics.SurveyEngine.setEmbeddedData('EndLoop', 1);
that.clickNextButton();
};
});
The button will not have the default style, thus, define a custom css to style your button to look like the buttons of your theme. The class name for the button I used here is id="css-class-mybutton", use .css-class-mybutton{ ... } in the css.
Hope that helps.
I know this is a bit late, but I had a similar issue and wanted to post an alternative solution. I used the loop and merge function, but I didn't want to have participants click on a button to exit the loop, instead I wanted to use a multiple choice question to exit the loop. This question was, "would you like to ask more questions?", with responses yes and no. If the participant selected "Yes" they would keep going through the loop and if they selected "No" they would exit the loop and go on to the next block of questions.
My first two steps are the same as those above.
1.Set up an Embedded data field named EndLoop and set the value to 1.
- in the survey flow panel select add an element. Then type in the name EndLoop.
- Then select set value now and set the value to 1.
- make sure that this is before your loop and merge block in the survey flow.
For each question in the loop and merge block use display logic to ensure that they only display when the embedded data element EndLoop is 1.
Now for the question "would you like to ask more questions?" add the following code in the add javascript area.
the javascript area can be found by clicking the advanced options button under the question number on the lefthand side of the screen. Select 'Add javascript...'
There will be some code already present...it should look like this:
Qualtrics.SurveyEngine.addOnload(function()
{
/Place Your Javascript Below This Line/
});
Basically I used event tracking to update the embedded data field EndLoop to the numerical value associated with the answer choice when the answer choice was selected. In this case the answer "Yes" had a value of 1 because it was the first option and "No" had a value of 2 because it was the second option. When "No" was selected, the embedded data field EndLoop was set to a value of 2. This then meant that none of the questions would display since they have display logic to ensure they only display when the EndLoop field is 1.
My entire code looks like this:
Qualtrics.SurveyEngine.addOnload(function ()
{
this.questionclick = function(event,element)
{
console.log(event, element);
if (element.type == 'radio')
{
var choiceNum = element.id;
if (choiceNum == 2)
{
Qualtrics.SurveyEngine.setEmbeddedData("EndLoop", choiceNum);
}
}
}
});
I used the second solution. Here's a minor correction for the script:
I wrote element.id.split('~')[2] instead of element.id, and it worked!
Qualtrics.SurveyEngine.addOnload(function ()
{
this.questionclick = function(event,element)
{
console.log(event,element);
if (element.type == 'radio')
{
var choiceNum = element.id.split('~')[2];
if (choiceNum == 2)
{
Qualtrics.SurveyEngine.setEmbeddedData("EndLoop", choiceNum);
}
}
}
});