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.
Related
I'm trying to have .child_box class to open and close slow but seems Local Storage is not respecting it. Either it wont open or it wont close. Without Local Storage, it works fine. Stumped.
The js:
$("document").ready(function () {
$(".manualclose").click(function () {
$(".child_box").toggle();
});
ls = localStorage.getItem('on')
if(ls) {
$(".child_box").show("slow")
}
$(".open_child").click(function () {
localStorage.setItem('on',true)
toggled = $(".child_box").toggle();
if(toggled.is(":hidden")) {
localStorage.clear();
}
});
$(".manualclose").click(function() {
localStorage.clear();
$(".child_box").hide("slow")
});
});
The button:
<div class="open_child" title="', $txt['sub_boards2'], '">
<i class="fas fa-plus-circle"></i>
</div>
localStorage isn't conflicting with toggle(). The problem is down to the way the browser schedules a reflow whilst executing JavaScript.
In this event handler
$(".open_child").click(function(){
localStorage.setItem('on',true)
toggled = $(".child_box").toggle(500);
if(toggled.is(":hidden")){
localStorage.clear();
}
});
your code toggles the .child-box element. It immediately goes to see if that element is now hidden.
The browser is running the animation that is caused by .toggle() and carries on executing the JavaScript. It checks whether the element is hidden, which it isn't because the animation hasn't completed yet, and so doesn't clear the localStorage. Only later when the animation completes would the element appear as 'hidden'.
You need to do things in a different order:
$(".open_child").click(function(){
let hidden =$(".child_box").is(":hidden");
if (hidden) {
$(".child_box").show(500);
localStorage.setItem('on',true)
} else {
$(".child_box").hide(500);
localStorage.removeItem('on');
}
});
This version checks the hidden status first, then shows or hides the element as required, and updates localStorage to match.
There is an alternative approach: use the complete function available to the jQuery .toggle() method to update localStorage. You'd still need to check to see what .toggle() has just done, so you don't gain much.
FWIW, I never use .toggle() precisely because I don't know what action it's performing.
A couple of other thoughts:
You're not declaring the variable you use, so they're being placed in the global context. This is a bad idea. Declare them in the functions they're used in with let.
localStorage stores strings, not other data type. JavaScript has coerced the data for you so you've got away with it, but good practice suggests that you should be more rigorous.
Using localStorage.clear() precludes the use of localStorage for any other purpose. Use localStorage.removeItem() instead.
I don't think it's localStorage. what i noticed:
a) you don't declare the variable ls.
b) why are you using a div as a button?
c) you don't use parse and stringify to get and set values in the localStorage.
d) you put a title in a DIV container
f) the title you set looks like PHP. it is missing the < ?php echo $text ... ;? > Tags
this is my very first question on Stackoverflow. I am currently developing a print function in my sap ui5 app to print out certain UI controls. I've got the function from here: http://embed.plnkr.co/jjyEPa1updkjBiNZqumS/preview
However, during runtime, when I click on the print button, my app only jumps to the method once and executes it correctly (to print). But after that, I can press the printbutton as often as I want, nothing happens and I can't find out why.
what the method does: i replace the body with a temporary body, which only contains the elements to be printed and execute window.print(). afterwards i insert the original body content again. Of course I use the UI controls to grab the HTML tags.
onPrintChart: function(oEvent){
var oTarget = this.getView(),
sTargetId = oEvent.getSource().data("targetId");
if (sTargetId) {
oTarget = oTarget.byId(sTargetId);
}
if (oTarget) {
var $domTarget = oTarget.$()[0],
sTargetContent = $domTarget.innerHTML,
sOriginalContent = $(document.body)[0].innerHTML;
$(document.body)[0].innerHTML = sTargetContent;
window.print();
$(document.body)[0].innerHTML = sOriginalContent;
} else {
jQuery.sap.log.error("onPrint needs a valid target container [view|data:targetId=\"SID\"]");
}
}
I managed to do it in a different, more elegant way without using a temporary body. I used CSS to hide all irrelevant elements (display: none) and keep only the relevant element for printing.
Apparently ui5 hung up when replacing the original body temporarily with another body. I noticed that ALL buttons didn't work anymore, not only the print button.
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
});
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');
I've got a simple javascript function which allows me to swap between different stylesheets in each page of my website. At the moment I don't have any cookies being implemented on the site so whenever I go to a new page the default stylesheet is loaded and if a user wanted to use the alternate stylesheet they would be forced to swap again. I want to make it to where by using cookies, whatever stylesheet the user chooses will remain when going to a different page.
Below I've got the following code which is my method of swapping stylesheets.
<script type="text/javascript">
var i=1;
function styleSheet(){
if(i%2==0){
swapper('css/main.css');
}
else{
swapper('css/stylesheetalternate.css');
}
i++;
}
</script>
<script>
function swapper(select_sheet){
document.getElementById('swapper').setAttribute('href', select_sheet);
}
</script>
So far I haven't been able to get it to where the webpage would remember what the user chose as their stylesheet theme and when reloading the page keep the same stylesheet. This is my first time working with cookies and I'm mostly just looking for a way to implement this with my original javascript.
EDIT: Also just as a heads up, I only know front end web programming at this point. Based on Dave A's answer, would I adapt my current styleSheet() function as follows or is this not even necessary anymore? One thing I've had a bit of a hard time understanding is how the key works.
<script type="text/javascript">
var i=1;
function styleSheet(){
if(i%2==0){
storeStyleSheet(sheet1, main.css);
}
else{
storeStyleSheet(sheet2, alternate.css);
}
i++;
}
</script>
<button onclick()="setStoredStyleSheet (styleSheetKey)">click here</button>
I would use HTML 5 local storage to store the CSS filename:
Store the user's selection with a function like:
function storeStyleSheet(styleSheetKey, StyleSheetName){
localStorage.setItem(styleSheetKey, StyleSheetName);
}
And pull and use the stored CSS, if there is one:
function setStoredStyleSheet(styleSheetKey){
var styleSheet = localStorage.getItem(styleSheetKey);
if(!(styleSheet=='undefined' || styleSheet==undefined) ){
swapper(styleSheet);
}
else{
swapper('css/stylesheetalternate.css');
}
}
I have found HTML 5 local storage to be easier to use than cookies. I have not had any problems using localStorage on any browser. And the interface is intuitive; just be aware that if the file/keyname was never stored before, it will return 'undefined' or undefined. There is also no automatic timeout with localStorage.
It would probably be easiest to pull in all the CSS (unless it's massive) and then apply a theme class to body/wherever you need it. And your CSS would look like
// common and default definitions
body.some-theme{
// theme overrides of default go here
}
body.some-other-theme{
// another theme
}
If you want a solution that remembers the user rather than the browser/ip address (remembering each browser has its own cookie jar) you'd probably best write this in a server side language with user preferences and only output the relevant CSS file using some server side processing.
However that's a much bigger question, to quickly solve the issue at hand consider the following.
Start by loading all CSS files, assuming they're not huge it probably won't make much difference speed wise, and it would mean the user could switch without the overhead of downloading extra data.
I agree with Dave A that the localStorage method is probably the best way to go about storing this info (without a server storing user preferences). So I suggest you write something similar to the following, I hacked this together rather quickly as I'm sure is rather apparent.
<!DOCTYPE html>
<html>
<head>
<style>
.default {
background-color: blue;
}
.alternative {
background-color: red;
}
</style>
<script type='text/javascript'>
function init() {
if (localStorage.getItem('cssName') !== "default") {
var i = 0;
var elems = [];
var elements = document.getElementsByClassName('default');
// Put the elements into an easy to interate array instead of HTML Collection.
for (i = 0; i < elements.length; i++) {
elems.push(elements[i]);
}
// Iterate over the new nice array.
elems.forEach(function(element, index, array) {
element.setAttribute('class', 'alternative');
});
}
}
</script>
</head>
<body onload='init();'>
<div class='default'>hbdfisubfiuodfhriubsrfiub</div>
<div>iboufruewhiobieiohhbefpijbepojb</div>
<div class='default'>hbdfisubfiuodfhriubsrfiub</div>
<div>iboufruewhiobieiohhbefpijbepojb</div>
<div class='default'>hbdfisubfiuodfhriubsrfiub</div>
<div>iboufruewhiobieiohhbefpijbepojb</div>
<div class='default'>hbdfisubfiuodfhriubsrfiub</div>
<div>iboufruewhiobieiohhbefpijbepojb</div>
</body>
</html>
I forgot to mention this if you wish to go down the local storage route: http://mozilla.github.io/localForage/?javascript#localforage
Might be worth reading about.