Problem :
I have an apex:commandButton on a VisualForce page, and a js file with some logic in it. Currently, this logic affects whether the button is visible or invisible on the page, using css. I need to change this so it makes the button enabled or disabled. However, whenever I try to disable the button in the js file, the onscreen button doesn't get affected. I am not experienced with front-end dev so may be missing or misunderstanding something obvious.
Button on .page file :
<apex:commandButton id="commitButton"
action="{!commitSelectedLines}"
value="{!$Label.commitSelectedLines}"/>
.js file (heavily edited down to the seemingly relevant bits) :
FFDCBalancer = (function(){
/**
* Singleton Balancer object to maintain balances on the page.
*/
var balancer = {
/** JQuery button component for the commit button. */
cmdCommit: undefined,
/** Set the enabled-ness of the commit button. */
setCommitEnabled : function(value) {
//I PRESUMABLY NEED TO CHANGE THIS BIT TO USE 'DISABLED'.
this.cmdCommit.css({
'display': value ? 'block' : 'none'
});
//I HAVE TRIED VARIOUS BITS OF CODE, SUCH AS THIS
//this.cmdCommit.disabled = value;
},
/**
* Respond to refresh by connecting event handlers and calculating the balances.
*/
onRefresh : function () {
var me = this;
me.cmdCommit = $FFDC('#commitButton');
}
};
$FFDC(document).on('FFDCRefresh', function(){ balancer.onRefresh(); });
return balancer;
}());
For this, you can do one of two things:
Use the {!$Component...commitButton} to get the client-side ID, though it can really mess JQuery up, since it adds a colon between the IDs, so you'll have to use it in combination with document.getElementById(...) - For example:
jQuery(document.getElementById('{!$Component.Form.commitButton}')).attr('disabled','disabled');
Put a "styleClass" on the button, and change it using that:
<apex:commandButton id="commitButton"
action="{!commitSelectedLines}"
value="{!$Label.commitSelectedLines}" styleClass="commitButtonClass" />
Then use jQuery to set the attribute:
jQuery(".commitButtonClass").attr('disabled','disabled');
Related
I have some custom JavaScript on my SquareSpace site that manipulates Product titles beyond what you can do with SquareSpace's default style editor. It works when initially loading the page (https://www.manilva.co/catalogue-accessories/) but if you click on any of the categories on the left, the styling resets to the default.
I'm assuming the JavaScript is being overwritten by the SquareSpace style, but I can't figure out why. Perhaps I'm calling the function in the wrong place?
Any suggestions would be helpful.
Thanks!
Current code:
document.querySelectorAll(".ProductList-filter-list-item-link".forEach(i=>i.addEventListener("click", function()
{
var prodList = document.querySelectorAll("h1.ProductList-title");
for (i = 0, len = prodList.length; i < len; i++)
{
var text = prodList[i].innerText;
var index = text.indexOf('-');
var lower = text.substring(0, index);
var higher = text.substring(index + 2);
prodList[i].innerHTML = lower.bold() + "<br>" + higher;
});
The source of your problem is that your template has AJAX loading enabled. There are currently a couple generally-accepted ways to deal with this as a Squarespace developer:
Disable AJAX loading
Write your javascript functions in a
manner that will run on initial site load and whenever an "AJAX load" takes place.
Option 1 - Disable AJAX:
In the Home Menu, click Design, and then click Site Styles.
Scroll down to Site: Loading.
Uncheck Enable Ajax Loading.
Option 2 - Account for AJAX in Your JS
There are a number of ways that developers approach this, including the following, added via sitewide code injection:
<script>
window.Squarespace.onInitialize(Y, function() {
// do stuff
});
</script>
or
<script>
(function() {
// Establish a function that does stuff.
var myFunction = function() {
// Do stuff here.
};
// Initialize the fn on site load.
myFunction();
// myFunction2(); , etc...
// Reinit. the fn on each new AJAX-loaded page.
window.addEventListener("mercury:load", myFunction);
})();
</script>
or
<script>
(function() {
// Establish a function that does stuff.
var myFunction = function() {
// Do stuff here.
};
// Initialize the fn on site load.
myFunction();
// Reinit. the fn on each new AJAX-loaded page.
new MutationObserver(function() {
myFunction();
// myFunction2(); , etc...
}).observe(document.body, {attributes:true, attributeFilter:["id"]});
})();
</script>
Each of those works for most of the latest (at time of writing) templates most of the time. Each of those have their advantages and disadvantages, and contexts where they do not work as one might expect (for example, on the /cart/ page or other "system" pages). By adding your code within the context of one of the methods above, and ensuring that the code is of course working in the desired contexts and without its own bugs/issues, you will have your code run on initial site load and on each AJAX page load (with some exceptions, depending on the method you use).
Your problem is the page does not reload when clicking a button on the left, just some elements are removed, added and replaced. The changed elements will not be restyled. You will need to re-run your JavaScript after one of those buttons is clicked. Perhaps something like this:
document.querySelectorAll(
".ProductList-filter-list-item"
).forEach(
i=>i.addEventListener(
"click", ()=>console.log("hello")
)
)
where you replace console.log("hello") with whatever resets your formatting.
I'm trying to add some functionality using Tampermonkey on top of a providers angular application but I'm stuck at this simple thing. I can't replicate the issue using CodePen so we're going to have to go for theories and suggestions. I'll try to be as specific as I can.
Adding this interval when the page loads to check when an input with the id serialNumberInput is available. Then I'm adding a dropdown to the form, and attach an onChange event to it to update the serial input field with the value of the selected option. However, the trigger parts just never happens. It does work when I enter them manually, but not with the script.
var populateSerialNumbersTimer = setInterval(function(){
var serial = $("input#serialNumberInput");
if($(serial).length >= 1){
$(serial).css("display", "inline").css("width", "50%");
$(serial).after(deviceToSerialSelectionHTML);
$("select#deviceToSerial").on("change", function(){
$(serial).val($("select#deviceToSerial").val());
$(serial).trigger("change");
$(serial).trigger("blur");
});
clearInterval(populateSerialNumbersTimer);
}
}, 200);
I've thought about it and considering how the serial number ends up in the text field the field must be accessible. Maybe it's that the events that I'm trying to trigger has not been declared at the time of the function declaration?
Suggestions much appreciated.
It looks like jQuery tries to cache the event somehow. This is how I solved it with native javascript in case someone else is interested:
function triggerEvent(e, s){
"use strict";
var event = document.createEvent('HTMLEvents');
event.initEvent(e, true, true);
document.querySelector(s).dispatchEvent(event);
}
$("select#deviceToSerial").on("change", function(){
serialNumberInput.val($("select#deviceToSerial").val());
triggerEvent("change", "input#serialNumberInput");
triggerEvent("blur", "input#serialNumberInput");
}
I run a WoW guild forum based on php (phpbb), javascript and html. Ever since long, Wowhead allows links to be posted to their item/spell IDs etc. The basic code to the Wowhead JS and it's variables is:
<script src="//static.wowhead.com/widgets/power.js"></script>
<script>var wowhead_tooltips = { "colorlinks": true, "iconizelinks": true, "renamelinks": true }</script>
There is an extension that puts this code in the footer of every page via a HTML file. Every Wowhead link posted will be converted in a link with a tooltip explaining what it links to. The '"renamelink": true' portion of the wowhead_tooltips variable makes it as such that any link of an item or spell is renamed to the exact name of what it is linked to.
The problem: when I generate custom URLs using a Wowhead link, ie:
Teleport
instead of displaying 'Teleport' with a tooltip of Blink, it will rename the entire URL to Blink with an icon, as described in the wowhead_tooltips variable.
What I want to achieve is:
Any direct URL to Wowhead should be converted into a renamed spell/item.
Any custom URL to Wowhead should be retain it's custom text, but retrieve the tooltip.
This should both be possible on a single page.
The best solution I have come up with is to add an 'if' function to var wowhead_tooltips based on class, then add the class to URLs:
<script>if ($('a').hasClass("wowrename")) { var wowhead_tooltips = { "colorlinks": true, "iconizelinks": true, "renamelinks": false } }</script>
<a class="wowrename" href="http://www.wowhead.com/spell=1953">Teleport</a>
This works, however, the problem with this solution is that once the script recognizes one URL with the class "wowrename" on the page it will stop renaming all links, meaning that custom URLs and direct URLs can't be mixed on a single page.
Any other solution I've tried, using IDs, defining different variables etc either don't work or come up with the same restriction.
Hence the question, is it possible to change Javascript variables (in this case "var wowhead_tooltips { "renamelinks": false}" per element (URL), based on id, class or anything else?
Direct link that gets renamed with tooltip and iccn.
Teleport
Custom link with tooltip and original text.
I've stored the original link text as a data attribute so we can restore it after it's been changed.
<a class="wowrename" href="http://www.wowhead.com/spell=1953" data-value="Teleport">Teleport</a>
Keep checking for when static.wowhead.com/widgets/power.js changes the last link text. Once changed, restore using the data-value value, remove the styling added that creates the icon and stop the timer.
$(function () {
//timmer
checkChanged = setInterval(function () {
// check for when the last link text has changed
var lastItem = $("a.wowrenameoff").last();
if (lastItem.text() !== lastItem.data('value')) {
$("a.wowrenameoff").each(function () {
//change value
$(this).text($(this).data('value'));
//remove icon
$(this).attr('style', '');
//stop timer
clearInterval(checkChanged);
});
}
i++;
}, 100);
});
This does cause the link icon to flicker on then off, but it is repeated after a page refresh.
JSFiddle demo
This is simple solution. It's not the best way.
var wowhead_tooltips = { "colorlinks": true, "iconizelinks": true, "renamelinks": true }
$('a').hover(function() {
if ($(this).hasClass('wowrename') {
wowhead_tooltips.renamelinks = true;
}
else {
wowhead_tooltips.renamelinks = false;
}
});
I don't know how exactly wowhead API works, but if wowhead_tooltips variable is loaded exactly in the moment when the user points the link with the mouse (without any timeout) - this can fail or randomly work/not work.
The reason can be that the javascript don't know which function to execute first.
I hope this will work. If it's not - comment I will think for another way.
You have to loop on all the links, like this:
$("a.wowrename").each(function() {
// some code
});
Here is what should happen:
I have a button with a label and an icon.
When I tap the button some actions will take place which will take some time. Therefore I want to replace the icon of the button with some loading-icon during the processing.
Normal Icon:
Icon replaced by loading gif:
So in pseudo code it would be:
fancyFunction(){
replaceIconWithLoadingIcon();
doFancyStuff();
restoreOldIcon();
}
However the screen isn't updated during the execution of the function. Here ist my code:
onTapButton: function(view, index, target, record, event){
var indexArray = new Array();
var temp = record.data.photo_url;
record.data.photo_url = "img/loading_icon.gif";
alert('test1');
/*
* Do magic stuff
*/
}
The icon will be replaced using the above code, but not until the function has terminated. Meaning, when the alert('1') appears, the icon is not yet replaced.
I already tried the solution suggested here without success.
I also tried view.hide() followed by view.show() but these commands weren't executed until the function terminated, too.
Let me know if you need further information. Any suggestions would be far more than welcome.
I finally found a solution displaying the mask during my actions are performed. The key to my solution was on this website.
In my controller I did the following:
showLoadingScreen: function(){
Ext.Viewport.setMasked({
xtype: 'loadmask',
message: 'Loading...'
});
},
onTapButton: function(view, index, target, record, event){
//Show loading mask
setTimeout(function(){this.showLoadingScreen();}.bind(this),1);
// Do some magic
setTimeout(function(){this.doFancyStuff(para,meter);}.bind(this),400);
// Remove loading screen
setTimeout(function(){Ext.Viewport.unmask();}.bind(this),400);
},
The replacing of the icons worked quite similar:
onTapButton: function(view, index, target, record, event){
//Replace the icon
record.data.photo_url = 'img/loading_icon.gif';
view.refresh();
// Do some magic
setTimeout(function(){this.doFancyStuff(para,meter);}.bind(this),400);
},
doFancyStuff: function(para, meter){
/*
* fancy stuff
*/
var index = store.find('id',i+1);
var element = store.getAt(index);
element.set('photo_url',img);
}
Thank you for your help Barrett and sha!
I think the main problem here is that your execution task is executing in the main UI thread. In order to let UI thread do animation you need to push your doFancyStuff() function into something like http://docs.sencha.com/touch/2.2.1/#!/api/Ext.util.DelayedTask
Keep in mind though, that you would need to revert it your icon only after fancy stuff is complete.
To update any button attributes you shoudl try to access the button itself. Either with a ComponentQuery or through the controllers getter. For Example:
var button = Ext.ComponentQuery.query('button[name=YOURBUTTONNAME]')[0];
button.setIcon('img/loading_icon.gif');
that shold update your button's icon.
also when you get a ref to the button you will have access to all the methods availble to an Ext.Button object:
http://docs.sencha.com/touch/2.2.1/#!/api/Ext.Button-method-setIcon
I have a web application and in it I am doing some client-side validation. This is done by adding to each Asp:TextBox
onkeyup="javascript: value_change(this);"
Once this gets to the value change I have this Javascript...
function value_change (text_box) {
// validate code here
if (valid) {
text_box.className = "normalInput";
document.getElementById("GoButton").disabled = false;
}
else {
text_box.className = "errorInput";
document.getElementById("GoButton").disabled = true;
}
}
The className corresponds to CSS Classes the salient portion of which look like this:-
.normalInput
{
background-color: #ffffff;
}
.errorInput
{
background-color: #ff0000;
}
This works fine and dandy when the page is initially displayed, but after the first postback, although the function is invoked, the classname set and the GoButton sensitivity set (I have demonstrated this by stepping through it with debug), the background colours do not change.
Does anyone know why this is and what I should do about it?
Edit taking #Pete's advice, I inspected the text_box immediately after the new classname was assigned to it, and it appears that the currentStyle attribute is remaining unchanged. The characteristics of the currentStyle are the same as the normalInput class; I tweaked them and re-ran it to check. So I deduce that the assignment is being ignored, rather than a different CSS working its way in somehow.
Styling changed on the client does not get sent back to the server. You'd have to set the properties on the server yourself to persist them. The server builds up the control definition from the set of properties available on the server, and re-renders the original value. This is because only the value of the textbox posts back to the server; everything else does not.
With that said, you could interpret the conditions to enable or disable the button, and set the appropriate CSS style, and set these properties on the server, or reapply the styles on document load.
After much swearing I finally got the validation to change the background colour after postback as follows:-
function value_change (text_box) {
// validate code here
if (valid) {
text_box.className = "normalInput";
document.getElementById(text_box.name).style.backgroundColor = "#ffffff";
document.getElementById("GoButton").disabled = false;
}
else {
text_box.className = "errorInput";
document.getElementById(text_box.name).style.backgroundColor = "#ff0000";
document.getElementById("GoButton").disabled = true;
}
}
I.e by setting it by hand. I don't like this very much as it is driving a coach-and-four throught the whole purpose of the CSS Classes. If someone can come up with something better I am all ears.