How to use dynamically created JavaScript object in a conditional statement? - javascript

I'm having this little bump in a function, normally if the variable "receivedRDB" is undefined in the DOM, it is dynamically created in a form and if it is defined it takes the value of another variable "receivedR". But "receivedRDB" keeps being created, even when it is already defined in the DOM.
var receivedRDB = document.getElementsByName('receivedR')[0];
if (typeof receivedRDB !== "undefined") {
receivedR = JSON.parse(receivedRDB.value.split(",").slice(0));
} else {
receivedR = [];
}
if (typeof receivedRDB !== "undefined") { //never detected
receivedR.push(toRemoveR);
receivedRDB.value = JSON.stringify(receivedR).replace(/"\[\\|\\"]|\\"/g, "");
} else { //problematic part
event.preventDefault();
receivedR.push(toRemoveR);
var input = document.createElement("input");
input.type = "hidden";
input.name = "receivedR";
input.value = JSON.stringify(receivedR);
rForm.appendChild(input);
}

Here's a solution based on the fact that getElementsByName is a "live" list
Anywhere in your code you can put
var receivedRDB = document.getElementsByName('receivedR');
then change your code to
if (receivedRDB.length !== 0) {
receivedR.push(toRemoveR);
receivedRDB[0].value = JSON.stringify(receivedR).replace(/"\[\\|\\"]|\\"/g, "");
} else {
event.preventDefault();
receivedR.push(toRemoveR);
var input = document.createElement("input");
input.type = "hidden";
input.name = "receivedR";
input.value = JSON.stringify(receivedR);
rForm.appendChild(input);
}

I guess <script> tag which uses receivedRDB appears earlier than the DOM (in <head> for example). If so, there is more than one way to solve.
<script defer>
add an attribute defer to the <script> tag will make the JavaScript code be run after DOM is loaded
window.onload = function(){ /* ... */ }
window.onload will be called after DOM is loaded.
window.addEventListener('load', function(){ /* ... */ })
More compatible one (more than one function can be called individually)
If not the problem please include the minimal code to reproduce the problem.

Related

Using “this” to select an element’s innerText

I’m a beginner, so I apologize if I’m missing something obvious.
I’m trying to write a simple web app. When you type text into a textarea and click a button, a “card” (div) is created with that textarea’s value as its innerText. When you click on a card, its innerText is then copied to the clipboard.
It works when I write out the function this way:
el.setAttribute(
"onclick",
"console.log(navigator.clipboard.writeText(this.innerText));"
);
But if I write out the function separately and call it when setting the attribute, undefined is copied to the keyboard:
el.setAttribute("onclick", "copyText()");
I’d stick with the first, working option, except for two things:
Primarily I’m just trying to learn, so avoiding difficulties because I don’t understand them doesn’t really make sense.
I want to add more lines of code to the copyText() function so that it works on mobile devices, too. Can I even do that the first way?
Here’s my code in full:
const app = document.getElementById("app");
function createCard() {
let input = document.getElementById("textarea").value;
if (input == "") {
console.log("You must enter text to create a card.");
} else {
const el = document.createElement("div");
el.innerText = document.getElementById("textarea").value;
el.setAttribute("class", "item card");
el.setAttribute("onclick", "copyText()");
app.appendChild(el);
document.getElementById("textarea").value = "";
}
}
function copyText() {
navigator.clipboard.writeText(this.innerText);
}
I expected it to work exactly the same as the following, but it doesn’t. It returns undefined.
const app = document.getElementById("app");
function createCard() {
let input = document.getElementById("textarea").value;
console.log(input);
if (input == "") {
console.log("You must enter text to create a card.");
} else {
const el = document.createElement("div");
el.setAttribute("class", "item card");
el.setAttribute(
"onclick",
"console.log(navigator.clipboard.writeText(this.innerText));"
);
el.innerText = document.getElementById("textarea").value;
app.appendChild(el);
document.getElementById("textarea").value = "";
}
}
I suspect it’s an issue with “this” and scope, but I can’t figure it out. Sorry again—I know this is very much a beginner’s question. Thanks for your help.
Calling copyText() calls it without a this context. You would need to use
el.setAttribute("onclick", "copyText.call(this)");
// ideally also pass the event object:
el.setAttribute("onclick", "copyText.call(this, event)");
However, the best practice is to install an event handler function instead of using the onclick attribute, so you should do
el.onclick = copyText;
// or
el.addEventListener("click", copyText);
Notice these refer to the copyText function that is in scope, it doesn't need to be a global variable.
const input = document.getElementById("textarea");
const app = document.getElementById("app");
function createCard() {
console.log(input.value);
if (!input.value) {
console.log("You must enter text to create a card.");
} else {
const el = document.createElement("div");
el.className = "item card";
el.onclick = function() {
console.log(navigator.clipboard.writeText(this.textContent));
};
el.textContent = input.value;
app.appendChild(el);
input.value = "";
}
}

Change element display none back to default style value JS

I have a function in JS that hides the element parsed:
function hide(id){
document.getElementById(id).style.display = "none";
}
How can I create a function that brings back the element to the default style value. For instance a div display property is "block" as for an image is "inline-block", other elements are "inline" or lists are "list-item" How can I bring them back their default state?
function show(id){
document.getElementById(id).style.display = "?????";
}
I know how to do it in Jquery but it is not an option.
In CSS there might be styles for the elements including style:none, which need to be overwritten to the default value.
Since there is CSS in my example making style.display = '' eliminates the style added with JS but gets back to whatever style is added in CSS, I want to bring it back to its default value even before assigning styles with CSS.
I tried this as it was suggested in a link in one of the comments:
elem = document.getElementById(id);
var theCSSprop = window.getComputedStyle(elem,null).getPropertyValue("display");
but in this case 'theCSSprop' returns "none" for a div, when I expect "block"
Any ideas?
Thanks.
You need just assign it to empty value:
document.getElementById(id).style.display = "";
Or using removeProperty method:
document.getElementById(id).style.removeProperty( 'display' );
But note that removeProperty will not work on IE<9.
If you want to get original CSS value you will need probably to get it from empty <iframe> element. I created example on jsFiddle how to get current value using getComputedStyle and iframe value on jsFiddle.
Please note that getComputedStyle not support old versions of IE. It support IE9+.
For IE8 you should use Element.currentStyle
Note:
If you define display:none; for a class or tag (either in a separate css file or in the head section), the above methods won't work.
Then you will have to determine which type of tag + class it is and manually assign the value specific to it.
These are examples of what may not work:
// In many cases this won't work:
function ShowHide_WillRarelyWork(id, bDisplay) {
// Note: This will fail if parent is of other tag than element.
var o = document.getElementById(id);
if (o == null) return;
//
if (bDisplay) {
o.style.display = 'inherit';
o.style.visibility = true;
}
else {
o.style.display = 'none';
}
} // ShowHide_WillRarelyWork(...)
// This will work in most, but not all, cases:
function ShowHide_MayWork(id, bDisplay) {
// Note: This will fail if element is declared as 'none' in css.
var o = document.getElementById(id);
if (o == null) return;
//
if (bDisplay) {
o.style.display = null;
o.style.visibility = true;
}
else {
o.style.display = 'none';
}
} // ShowHide_MayWork(...)
This is long but will most probably work:
function getDefaultDisplayByTag(sTag) {
// This is not fully implemented, as that would be very long...
// See http://www.w3.org/TR/CSS21/sample.html for full list.
switch (sTag) {
case 'div':
case 'ul':
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6': return 'block';
//
case 'li': return 'list-item';
//
case 'table': return 'table';
//
case 'td':
case 'th': return 'table-cell';
}
// Fallback:
return 'block';
} // getDefaultDisplayByTag(...)
//
function computeDisplay(o) {
var oFunction = window.getComputedStyle;
if (oFunction) {
var oStyle = window.getComputedStyle(o)
if ((oStyle) && (oStyle.getPropertyValue)) {
return oStyle.getPropertyValue('display');
}
}
if (window.currentStyle) {
return window.currentStyle.display;
}
return null; // <-- This is going to be a bad day...
} // computeStyle(...)
//
// This will most probably work:
function ShowHideObject_WillProbablyWork(o, bDisplay, bMaybeRecursive) {
if ((o == null) || (o == undefined) || (o == document) || (o.tagName == undefined)) return;
//
if (bDisplay == null) bDisplay = true
if (!bDisplay) {
o.style.display = 'none';
}
else {
// First remove any directly set display:none;
if ((o.style.display == 'none') || (o.style.display == '')) {
o.style.display = null;
}
//
var sDisplay = null;
var sDisplayCurrent = computeDisplay(o);
var oParent = o.parentNode;
// Was this element hidden via css?
if ((sDisplayCurrent == null) || (sDisplayCurrent == 'none')) {
// We must determing a sensible display value:
var sTag = o.tagName;
sDisplay = getDefaultDisplayByTag(sTag);
} // else: if ((sDisplayCurrent != null) && (sDisplayCurrent != 'none'))
//
// Make sure visibility is also on:
if (sDisplay != null) o.style.display = sDisplay;
o.style.visibility = true;
//
if (bMaybeRecursive) {
// We should travel up the tree and make sure parent are also displayed:
ShowHideObject_WillProbablyWork(oParent, true, true);
}
} // else: if (!bDisplay) ...
//
} // ShowHideObject_WillProbablyWork(...)
//
// ... and finally:
function ShowHideId_WillProbablyWork(id, bDisplay, bMaybeRecursive)
var o = document.getElementById(id);
ShowHideObject_WillProbablyWork(o, bDisplay, bMaybeRecursive)
} // ShowHideId_WillProbablyWork(...)
Of course this could be shortened a bit; but that's how it looks in my source.
Here is one more solution for retrieving any property default value of any element. Idea is following:
Get nodeName of specific element
Append a fake element of the same node name to body
Get any property value of the fake element.
Remove fake element.
function getDefaultValue(element, property) {
var elDefault = document.createElement(element.nodeName);
document.body.appendChild(elDefault);
propertyValue = window.getComputedStyle(elDefault,null).getPropertyValue(property);
document.body.removeChild(elDefault);
return propertyValue;
}
function resetPropertyValue (element,propertyName) {
var propertyDefaultValue = getDefaultValue(element, propertyName);
if (element.style.setProperty) {
element.style.setProperty (propertyName, propertyDefaultValue, null);
}
else {
element.style.setAttribute (propertyName, propertyDefaultValue);
}
}
#d {
background: teal;
display: inline;
}
<button onclick="resetPropertyValue(document.getElementById('d'), 'display')">Try it</button>
<div id="d">test</div>
You can use custom attributes
function hide(id){
var elem = document.getElementById(id);
//Store prev value
elem.setAttribute('custom-attr', elem.style.display);
elem.style.display = "none";
}
function show(id){
var elem = document.getElementById(id);
//Set prev value
elem.style.display = elem.getAttribute('custom-attr');
}
Filling in an empty value removes the inline override, so the original value is active again.
function show(id){
document.getElementById(id).style.display = "";
}
Since what you want is the default value for the element and not what's in the style sheet, you simply want to set the value to auto.
document.getElementById(id).style.display="auto"
This tells the browser to calculate what the normal display for this type of element is and to use that.

Detect text input

I am trying to understand this JS function:
JS Fiddle Demo
I basically got it out of a book that I am trying to learn from. The book is called "JavaScript: The Definitive Guide" (pg484). But the function does not include the html that goes with it. I'd appreciate if someone could help me write the html that would make this work, thereby I might be able to get a better understanding how it works. I have made a stab at this with the link above.
I really don't like this book the way it does this. It happens a lot. I am a novice, does anyone have advice of what to do other than just come on here and try and get an answer.
Appreciate any help.
//Example 17-7. Using the propertychange event to detect text input
function forceToUpperCase(element) {
if (typeof element === "string") element = document.getElementById(element);
element.oninput = upcase;
element.onpropertychange = upcaseOnPropertyChange;
// Easy case: the handler for the input event
function upcase(event) { this.value = this.value.toUpperCase(); }
// Hard case: the handler for the propertychange event
function upcaseOnPropertyChange(event) {
var e = event || window.event;
// If the value property changed
if (e.propertyName === "value") {
// Remove onpropertychange handler to avoid recursion
this.onpropertychange = null;
// Change the value to all uppercase
this.value = this.value.toUpperCase();
// And restore the original propertychange handler
this.onpropertychange = upcaseOnPropertyChange;
}
}
}
Related HTML might be:
<input type="text" id="i0">
<script>
window.onload = function() {
forceToUpperCase('i0')
}
</script>
However, I'm not sure what the function is doing is useful or that the listener attachment and detachment methods used are robust. But it might be useful for understanding certain aspects of events and listeners.
<!DOCTYPE html>
<html>
<head>
<script>
var element = document.getElementbyId(Java_C#);
function forceToUpperCase(element) {
if (typeof element === "string") element = document.getElementById(element);
element.oninput = upcase;
element.onpropertychange = upcaseOnPropertyChange;
// Easy case: the handler for the input event
function upcase(event) { this.value = this.value.toUpperCase(); }
// Hard case: the handler for the propertychange event
function upcaseOnPropertyChange(event) {
var e = event || window.event;
// If the value property changed
if (e.propertyName === "value") {
// Remove onpropertychange handler to avoid recursion
this.onpropertychange = null;
// Change the value to all uppercase
this.value = this.value.toUpperCase();
// And restore the original propertychange handler
this.onpropertychange = upcaseOnPropertyChange;
}
}
}
</script>
</head>
<body>
<p id="Java_C#">
Public static void main{}
</p>
</body>
</html>

Same JavaScript for multiple instances of user control not working

I am using a user control in my website which performs the functionality of auto complete textbox. I have used JavaScript for the keydown and onfocus client events. This is the code:
<script language="JavaScript" type="text/javascript">
function TriggeredKey(e) {
var keycode;
if (window.event) keycode = window.event.keyCode;
if (keycode == 9) {
document.getElementById("<%=pnlSearch.ClientID %>").style.visibility = 'hidden';
document.getElementById("<%=pnlSearch.ClientID %>").style.display = 'none';
}
else {
document.getElementById("<%=hdfkey.ClientID %>").value = keycode;
}
_dopostback();
}
function pasteIntoInput(el) {
var text = document.getElementById("<%=txtSearch.ClientID %>").value;
if (typeof text != "undefined" && text != "") {
el.focus();
el.value = el.value;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
var val = el.value;
var selStart = el.selectionStart;
el.value = val.slice(0, selStart) + val.slice(el.selectionEnd);
el.selectionEnd = el.selectionStart = selStart + text.length;
}
else if (typeof document.selection != "undefined") {
el.focus();
}
}
}
When I use a single instance of this control in my aspx page it works fine but when I use more than one instances in my aspx page the JavaScript of all of the controls is overwritten by the last instance of the control in my page and no other control works.
Here's how I've dealt with problems like this in the past...
A block like this goes in an external js file referenced in the control ascx.
function UserControl() {
}
UserControl.prototype = {
DoStuff : function() {
var x = this.clientID;
window.alert(this.pnlSearchClientID);
},
TriggeredKey : function(e) {
var keycode;
if (window.event) keycode = window.event.keyCode;
if (keycode == 9) {
document.getElementById(this.pnlSearchClientID).style.visibility = 'hidden';
document.getElementById(this.pnlSearchClientID).style.display = 'none';
}
_dopostback();
},
pasteIntoInput : function() {
var text = document.getElementById(this.txtSearchClientID).value;
}
};
A block like this goes in the ascx file:
<script type="text/javascript">
function UserControl<%=this.ClientID%>() {
this.pnlSearchClientID = <%=pnlSearch.ClientID%>;
this.txtSearchClientID = <%=txtSearch.ClientID%>;
}
UserControl<%=this.ClientID%> = UserControl.prototype;
</script>
And then in the page including the user control:
<script type="text/javascript">
var inst1 = new UserControl<%=instance.ClientID %>();
inst1.DoStuff();
</script>
The idea is that you have a base class with the functionality you need, shared across all instances of the user control. Then a derived class per instance of the user control, with a new constructor setting properties for all of the instance-specific date (ie the ids of the controls composing the user control). The base class references these properties. The derived class is named using the ClientID of the user control, making it unique on the page.
I don't have access to an asp.net ATM so there are probably errors in here...
First off i would define your javascript functins in one place, perhaps the parent page or even a globally referenced file. That way you dont have the same functions rendered over and over again when you use multiple instances of your user control on a single page.
Then instead of embedding the client IDs of your pnlSearch and txtSearch controls into the JavaScript functions I would recommend passing them into the functions whenever they are called.
The way you have it set up the JavaScript functions on the last instance of your user control that is rendered will be the ones that will be invoked every time, which will cause the functions in the previously rendered user control instances to be ignored.

Programmatic creation of text input with JavaScript

I am currently learning JavaScript, and I got stuck at the following problems: I have tried to dynamically create an input of type text from JavaScript and to set its onChange method, but it is fired only when the page is loaded. In addition, document.onload does not work for creating my input, but window.onload does, although the tutorials I read claim that these two are almost the same thing. The code is the following:
<html>
<head>
<script type="text/javascript">
function f(value) {
alert(value);
return true;
}
window.onload = function() {
if (document.getElementById("cloned") == null) {
var clonedInput = document.createElement('input');
clonedInput.type = 'text';
clonedInput.value = "";
clonedInput.id = 'cloned';
clonedInput.size = 20;
clonedInput.onChange = f(clonedInput.value);
var lastChild = document.getElementById("parent");
document.body.insertBefore(clonedInput, lastChild);
}
};
</script>
</head>
<body>
<input id="toClone" type="text"/>
<div id="parent"></div>
</body>
</html>
f(clonedInput.value) calls the function immediately and sets its return value (which is true in this case) as event handler.
You want to use an anonymous function:
clonedInput.onchange = function(){ f(this.value); };
Note: While HTML attributes are case-insensitive, JavaScript object properties are not.
You can actually clone the existing input element:
if (document.getElementById("cloned") == null) {
var clonedInput = document.getElementById("toClone").cloneNode(true);
clonedInput.id = 'cloned';
clonedInput.onchange = f;
var lastChild = document.getElementById("parent");
document.body.insertBefore(clonedInput, lastChild);
}
Now to read the value change f to this:
function f() {
var value = this.value;
alert(value);
return true;
}
Edit: to make the cloned input reflect the first "live" you'll have to attach couple of events:
if (document.getElementById("cloned") == null) {
var orgInput = document.getElementById("toClone");
var clonedInput = orgInput.cloneNode(true);
clonedInput.id = 'cloned';
clonedInput.onchange = f;
orgInput.onkeypress = ReflectValue;
orgInput.onchange = ReflectValue;
var lastChild = document.getElementById("parent");
document.body.insertBefore(clonedInput, lastChild);
}
Then have this function:
function ReflectValue() {
var cloned = document.getElementById("cloned");
cloned.value = this.value;
}
Now whenever user type in the first input, it will reflect in the second input, and whenever user focus out it will also reflect any "non-keyboard" changes like pasting text.

Categories