I'm new to programming so I don't know much, but I was wondering if there is a way to permanently disable a button. I've used this:
document.getElementById("WhateverTheIdIs").disabled = true;
but at some parts of the document I reverse that by using this:
document.getElementById("WhateverTheIdIs").disabled = false;
I need something that can override that, and just permanently disable the button. Is there a way to do this?
There is only one way to disable or enable a button. So, if you're looking for a separate way to permanently disable a button (yet leave it visible) that will override setting .disabled to true - there is no such property or feature for a button.
There are many ways to fix your code. For example, when you want to permanently disable a button, you can set a custom attribute on the button and then everywhere you think about enabling the button, you can just check that custom attribute and if it's set, don't enable the button.
Other options you could consider are hiding the button or removing the button from the DOM.
You can fix your code by making your own permanent disable in your own code. If, rather than manipulating the .disabled property directly, you just switch to using these functions, then your code would respect the .permDisabled property.
function getElem(elem) {
if (typeof elem === "string") {
elem = document.getElementById(elem);
}
return elem;
}
function disableButtonPermanent(elem) {
elem = getElem(elem);
elem.disabled = true;
elem.permDisabled = true;
}
function disableButton(elem) {
elem = getElem(elem);
elem.disabled = true;
}
function enableButton(elem) {
elem = getElem(elem);
if (!elem.permDisabled) {
elem.disabled = false;
}
}
You're trying to push too much logic into the DOM. This is very bad practise. Make sure your data model matches that what you're modelling. Have a boolean: all_buttons_are_disabled and then a boolean button_X_is_disabled then do something like:
function updateButtonX() {
document.getElementById("WhateverTheIdIs").disabled =
all_buttons_are_disabled || button_X_is_disabled;
}
http://jsfiddle.net/3n3w3/
toggleX will disable/enabled buttonX, but when you press disable all all buttons are permanently disabled.
Related
I am creating a Chrome Extension similar to the "Search on Google" when you right click on a selected text. However, I need mine to also work when right clicking on a mailto: e-mail link. How can I select the innerHTML, to select the e-mail address, and pass this information onto the extension to be searched?
I managed to make it work with the selected text (when highlighting text on the website) and right-clicking, but not when right-clicking on a hyperlinked e-mail address.
for(var i=0; i<numentries; i++)
{
//alert(_all[i][3]);
if(_all[i][3])
{
_all[i][0] = chrome.contextMenus.create({"title": _all[i][1], "contexts":["selection", "link"], "onclick": searchOnClick});
//alert("Menuitem created");
}
else _all[i][0] = -1;
}
var ask_options = getItem("_askoptions")=="true"? true : false;
if(ask_options){
//show separator
chrome.contextMenus.create({"type": "separator", "contexts":["selection", "link"]});
//show the item for linking to extension options
chrome.contextMenus.create({"title": "Options", "contexts":["selection", "link"], "onclick": function(){chrome.tabs.create({"url":"options.html"});}});
}
}
function searchOnClick(info, tab)
{
var itemindex = 0;
for(var i=0; i<numentries; i++)
{
if(info.menuItemId == _all[i][0])
{
//alert(i);
itemindex = i;
}
}
var ask_fg = getItem("_askbg")=="true"? false : true;
var ask_next = getItem("_asknext")=="true"? true : false;
var index = 1000;
var targetURL = _all[itemindex][2].replace("TESTSEARCH", info.selectionText);
targetURL = targetURL.replace("%s", info.selectionText);
Right now, it's only searching for the selection. When I attempt to search for a e-mail address hyperlink, the searched word is "undefined".
I need to change "undefined" to the e-mail address in the hyperlink.
Here is what I need to happen: https://i.imgur.com/2qJrwmk.png
You need to add an event listener for the contextmenu.
Using the example cat gave, I created a quick jsfiddle:
https://jsfiddle.net/kds2Lze8/
The code below adds the event listener to the document and is triggered on right click. Using that event you can then get the source element and ultimately the innerHTML.
Hope it helps!
document.addEventListener('contextmenu', function(ev) {
ev.preventDefault();
alert(ev.srcElement.innerHTML);
return false;
}, false);
I'm not sure about some of the Chrome-extension-specific stuff (and your snippet is giving an error that I had trouble debugging without your HTML markup), but I think this script will demonstrate how to do what you want.
Edit:
You did indeed say you wanted to know how to run the script in response to a right-click, but I omitted that part. Sorry about that. The revised version should clarify that. It logs the innerHTML of the clicked element (although not on left-clicks) if the element is an anchor whose href attribute starts with mailto:.
// Run the 'checkAnchorForEmail' function on non-primary click events
document.addEventListener("auxclick", checkAnchorForEmail);
function checkAnchorForEmail(event){ //'event' will be our local name for any event that triggers this function
// Refer to the event's target element as 'clickedElement'
let clickedElement = event.target;
// If the element was an anchor with an 'href' attribute...
if(clickedElement.tagName.toLowerCase() === "a" && clickedElement.href){
// Define a string to identify anchors with emails
let comparedString = "mailto:";
// Only respond if the href begins with that particular string
if(clickedElement.href.indexOf(comparedString) === 0){
// Now we know the user right-clicked* an `a` element with an email address and can act accordingly
console.log(clickedElement.innerHTML);
}
}
}
// *Note that the 'auxclick' event is triggered by any non-primary button click. To isolate right-clicks, the 'contextmenu' event may be useful.
test#google.com<br />
google docs<br />
test2#google.com
One other thing:
If you need to prevent the context menu from appearing until your script has completed its tasks, you can use event.preventDefault();, but then you would need to show the menu manually later. One way to do this is by firing the 'contextmenu' event on the target element.
It's possible that doing so would cause this script to run again, creating an infinite loop. If this happens, you might try calling the preventDefault method conditionally like this (untested):
function checkAnchorForEmail(event){
// The above code goes here...
// Now we know the user right-clicked* an `a` element with an email address and can act accordingly
if(event.target.dataset.ready != "true"){ // Check the data-ready attribute
// event.preventDefault();
// event.target.dataset.ready = "true" // Set the data-ready attribute
// Make your changes to the context menu here
}
else{
// The changes have already been made, so show the context menu here
// (maybe using a technique like the one in the link below)
}
}
Here is a suggestion for using the MouseEvent interface to open the context menu, as mentioned in the in-code comments.
I'm currently trying to write a userscript on Tampermonkey for a web based game. Using jQuery or JS, I would like to make a span with id='#notification-static-woodcutting' a clickable link that runs a pre-written function that the host made. The function is openTab('woodcutting').
Here's the catch:
I only want the link to happen when the game removes the style "display:none;".
I don't have acccess to the openTab() function, nor can I edit the HTML.
$(document).ready(function() {
var woodcuttingNotification = document.getElementById('notification-static-woodcutting');
while( $(woodcuttingNotification).css('display') === null ) {
$(woodcuttingNotification).css('cursor','pointer');
$(woodcuttingNotification).click(function() {
$(woodcuttingNotification).attr('onClick',"openTab('woodcutting')");
});
}
});
Ideally, I am trying to produce
<span style id="notification-static-woodcutting" onclick="openTab('woodcutting')">
If the game takes away the style="display:none;"... the game automatically adds and takes away the style.
The cursor doesn't change, nor does anything happen when I click, which leads me to believe that my if(selector) is wrong?
I am still not 100% clear of the question, but here is a try,
Have kept a flag to check the display status of the span, and I have a kept jquery event handler where we can check the state of the flag.
var flag = false;
$(document).ready(function() {
$(document).on('click','#notification-static-woodcutting',function(){
if(flag == true)
openTab('woodcutting');
});
setInterval(function(){
if($('#notification-static-woodcutting').css('display') == 'none')
flag = false;
else
flag = true;
},100);
});
Adjust the time interval as required.
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.
I have an accordion set up to handle registration. I am wanting to validate the data entered on each panel when the user clicks on a different panel tab. I have a continue button on each panel, and am able to validate to my heart's content when the user uses that to go to the next panel.
My problem is that they can also click independently on the accordion tabs (and I want them to be able to skip around for editing purposes), but I would like to validate on those events too.
I've done a bunch of searching, but have not found a satisfactory answer. I am fairly new to Javascript and super-brand-new to jQuery, so please, if you have code snippets for me, be thorough in explaining them.
This should be a straightforward problem (similar to on-click, etc.). I'm quite surprised and frustrated that I haven't found an answer yet.
Edit:
Eric, I couldn't get this to work. Here is my version. I put it in the head section. I have some test code in there that has worked reliably for me in the past (changing the label on one of the tabs). I'm assuming this code has worked for you? Anyway, thanks for your help and I hope we've understood each other sufficiently.
// add capability to detect when accordion tab has been clicked
RegFormAccordion.addEventListener('click', function(e){
var btnElement;
(function findAccordionButton(el){
//e.target is the original element actually clicked on
//the event bubbles up to ancestor/parent nodes which is why you can listen at
//the container
if(!btnElement){ btnElement = e.target; }
else { btnElement = el; }
if(e.target.className !== 'accordionBtn')
{
findAccordionButton(btnElement.parentNode);
}
else
{
var curr_panel_index = RegFormAccordion.getCurrentPanelIndex();
document.getElementById("verify-reg-panel-label").innerHTML = "Index = " + curr_panel_index; // test code to see if it's even getting here
if (curr_panel_index == 1) // contact section
{
ValidateContact();
}
else if (curr_panel_index == 2) // payment section
{
ValidatePayment();
}
UpdateVerifyPanel(); // update contents of verification panel
}
})()
} );
Event delegation.
someAccordianContainer.addEventListener('click', function(e){
var btnElement;
(function findAccordionButton(el){
//e.target is the original element actually clicked on
//the event bubbles up to ancestor/parent nodes which is why you can listen at
//the container
if(!btnElement){ btnElement = e.target; }
else { btnElement = el; }
if(e.target.className !== 'accordionBtn'){
findAccordionButton(btnElement.parentNode);
}
else { doSomething(btnElement); }
})()
} );
You will have to normalize for IE<=8 however if you're supporting older browsers, since it uses a proprietary attachEvent method. Hit quirksmode.org for the details or just use something like jQuery or MooTools.
OK. I found the function that SpryAccordion.js uses to open a new panel and added my own code. Simple and elegant. It's not what I would normally do (usually I leave "libraries" alone). But if you make it editable without giving me another way to take needed control, then the hack is gonna happen.
If I need to use another accordion somewhere else on my website, I will have to double check that I have the correct accordion before invoking the hack. A trade-off I'm willing to make. It works perfectly now. Here is the code:
Spry.Widget.Accordion.prototype.openPanel = function(elementOrIndex)
{
var panelA = this.currentPanel;
var panelB;
if (typeof elementOrIndex == "number")
panelB = this.getPanels()[elementOrIndex];
else
panelB = this.getElement(elementOrIndex);
if (!panelB || panelA == panelB)
return null;
// Start Becca's code
var panelIndex = this.getPanelIndex(panelA);
if (panelIndex == 1) // contact info panel
{
if (ValidateContact())
UpdateVerifyPanel();
else
return null;
}
else if (panelIndex == 2) // payment info panel
{
if (ValidatePayment())
UpdateVerifyPanel();
else
return null;
}
// End Becca's code
var contentA = panelA ? this.getPanelContent(panelA) : null;
var contentB = this.getPanelContent(panelB);
...
...
...
};
Yes, all I wanted was the same control over the panel tabs as I have over my own user-defined buttons, to make sure I could both validate before moving on, and to update my verification screen after any edit the user makes, not just the ones where they happen to hit my continue button. I'm a happy camper. So glad I took a couple of days off.
I hope this helps someone get more control over their own accordions. So glad I don't have to do a crash-course on jQuery when all I want right now is to get my blasted website up.
On a page with Ajax event, I want to disable all actions until the Ajax call returns (to prevent issues with double-submit etc.)
I tried this by prepending return false; to the current onclick events when "locking" the page, and removing this later on when "unlocking" the page. However, the actions are not active any more after they are "unlocked" -- you just can't trigger them.
Why is this not working? See example page below. Any other idea to achieve my goal?
Example code:
both the link and the button are showing a JS alert; when pressing lock, then unlock the event handler is the same as it was before, but doesn't work...?!?
The code is meant to work with Trinidad in the end, but should work outside as well.
<html><head><title>Test</title>
<script type="text/javascript">
function lockPage()
{
document.body.style.cursor = 'wait';
lockElements(document.getElementsByTagName("a"));
lockElements(document.getElementsByTagName("input"));
if (typeof TrPage != "undefined")
{
TrPage.getInstance().getRequestQueue().addStateChangeListener(unlockPage);
}
}
function lockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.cursor = 'wait';
if (el[i].onclick)
{
var newEvent = 'return false;' + el[i].onclick;
alert(el[i].onclick + "\n\nlock -->\n\n" + newEvent);
el[i].onclick = newEvent;
}
}
}
function unlockPage(state)
{
if (typeof TrRequestQueue == "undefined" || state == TrRequestQueue.STATE_READY)
{
//alert("unlocking for state: " + state);
document.body.style.cursor = 'auto';
unlockElements(document.getElementsByTagName("a"));
unlockElements(document.getElementsByTagName("input"));
}
}
function unlockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.cursor = 'auto';
if (el[i].onclick && el[i].onclick.search(/^return false;/)==0)
{
var newEvent = el[i].onclick.substring(13);
alert(el[i].onclick + "\n\nunlock -->\n\n" + newEvent);
el[i].onclick = newEvent;
}
}
}
</script>
<style type="text/css">
</style>
</head>
<body>
<h1>Page lock/unlock test</h1>
<p>Use these actions to lock or unlock active elements on the page:
lock,
unlock.</p>
<p>And now some elements:</p>
<a onclick="alert('This is the action!');return false;" href="#">link action</a>
<input type="button" value="button action" onclick="alert('This is another action!')"/>
</body>
</html>
Thanks guys for your ideas and answers.
Now I see that I have mixed up Strings and functions, which obviously can't work ;(
I should have made clear that we use some Web FW and tag libraries (Trinidad) which create the event handling (and Ajax) code, hence I can't edit that directly or use synchronous Ajax etc.
Moreover, Ajax is only one scenario where this code should be executed. It's purpose is to prevent the user to double-submit a page/action, which is also relevant for non-Ajax pages where you could kind of doulbe-click on a button. I know that this is not really safe, and it's only meant to be a "convenience" thingy to avoid getting the navigation error page too often (we have server-side protection, of course).
So, will try the div overlay, probably.
Thanks again,
Christoph.
How about setting up a global var
actions_disabled = 0
increment when the AJAX call starts then decrement when it finishes. All your "action" handlers can then start with
if (actions_disabled) return false;
Much simpler than debugging self-modifying code!
Alternatively, to lock your controls you could set:
control.disabled="disabled"
which will have the bonus of greying them out, making it obvious to the user that they can't submit. To unlock, simply set:
control.disabled=""
NEW IDEA BASED ON COMMENTS (can't quote code in comments, it appears ...):
You can always just hang extra attributes off Javascript objects:
To lock, you could:
control.onclick_old = control.onclick
control.onclick = "return false;"
To unlock, you could:
control.onclick = control.onclick_old
I once achieved this goal by creating a DIV that covered the area I wanted disabled, setting its z-index higher than any of the other elements on the page, and then setting its opacity to 0. By default, this DIV was hidden by display: none, so that it wouldn't interfere with anything. However, when I wanted the area disabled, I just set its display to block.
Steve
AJAX. Asynchronous. Just make the HTTP request synchronous. Problem solved.
The problem with your code is a result of not coming to grips with types in javascript.
When you say:
var newEvent = 'return false;' + el[i].onclick
what this does is coerce el[i].onclick (which is a function) to a string, then concatenates it to the string 'return false;'. Then when you reassign it as so:
el[i].onclick = newEvent;
onclick which was previously a function is now a string.
Then you attempt to resurrect your old function from the string by taking a substring:
var newEvent = el[i].onclick.substring(13);
which is fine, except newEvent is still a string! So when you assign it back to onclick again, you are assigning the string representation of the original function, not the function itself.
You could use eval to evaluate the string and return the function, but please don't do that. There are a number of better ways to do this, as has been suggested by other commenters.
I would also question why you wish to use AJAX at all if you don't want to allow asynchronous requests.
Put lockPage() at top of activete() function, and unlockPage() at bottom of deactivate().
activate: function() {
function lockPage()
{
lockElements(document.getElementsByTagName("a"));
lockElements(document.getElementsByTagName("input"));
lockElements(document.getElementsByTagName("button"));
};
function lockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.pointerEvents="none";
}
};
lockPage();
// ...
},
deactivate: function() {
// ...
function unlockPage() {
unlockElements(document.getElementsByTagName("a"));
unlockElements(document.getElementsByTagName("input"));
unlockElements(document.getElementsByTagName("button"));
};
function unlockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.pointerEvents="auto";
}
};
unlockPage();
},
Using a div overlay does not prevent a user from tab-ing into your page. Usually that is OK, since most users do not tab through a page anyhow.
If you use any keyboard shortcuts on your page, they will still be available, so separate handling will be needed for those.
Alse, I assume that clicking an element that can have focus (eg. an <a> tag), then pressing enter, would still cause a double submit.