Using HTML id to compare to JS variable? - javascript

JS:
var player = {
su11: 100,
su22: 1000,
}
function gLoop() {
$(".upgrade").each(function() {
var test = player.obj[$(this).attr("id")];
if(player.total >= test)
{
$(this).prop("disabled", false);
}
});
}
setInterval(gLoop, 50);
HTML:
<button id="su11" class="upgrade su"></button>
The current basic code I have is as above. In the html, each instance of the .upgrade class has an id of the format "su##", and the player has a series of values of the same name.
My main concern is how I am using the id to reference the corresponding value in player. Is there an issue with the code that is unrelated to that, or is that simply a very bad idea?
My main goal is a button that, when a value is greater than or equal to its cost, enable itself to be clicked. It would be checked periodically as part of the main game loop. If there's a better way to do this (and I'm almost sure there is), please do tell me; I'm still relatively new to JS/JQuery.

The ID tag is used for storing an unique identifier, which can be used to find the element (and no other elements) in the document. Don't store arbitrary information in this tag. It goes against the goal of the ID tag, and also provokes the situation that at a certain time there are multiple elements in the document with the same ID (in a case where multiple elements have the same information payload).
If you really want to connect data to an DOM element, the common way to do it would be to use an data-xx attribute with your element such as <element data-su="12">, which you could then read with player.object[$(this).data("su")].

Related

How to get variable, array, nodelist from different function?

I am trying to write a ToDoList with JavaScript.
I have an input-element. Whenever I type something and press enter, it creates a new fieldset(in my example its a fieldset but it can also be a Div) with the class name ".fieldListClass" and a P-Tag as a child of fieldset. the P-tag innerHTML is the the value of input. I used Click-EventListener for that.
After each click, I assigned the query selector of all .fieldListClass to a nodeList "fieldListQuery". I even converted this nodeList into an Array but no result.
Now I want to create an addEventListner but outside the previous one. it should be a new one. And It should be a click-EventListener for all fieldListQuery which where created inside the previous function.(this part is at the bottom of my code)
When I click on it something should happen like removing the current target etc. But it wont work because outside the function it always says that this variable is undefined. I don't get it because I declared it global outside of the function.
I don't want to use DOMNodeInserted or MutationObserver yet for detecting changes inside the DOM. Simple because the first one is not recommended anymore it and the last one I have no idea how to use it. Many people saying that this is not a safe way.
Any Help please?
let addDiv = document.createElement("div"); addDiv.id = "addDivId";
let listDiv = document.createElement("div"); listDiv.id = "listDivId";
let inputText = document.createElement("input"); inputText.id = "inputTextId";
let fieldList; // = document.createElement("fieldset");
let fieldDiv; // = document.createElement("div");
let fieldDivP; // = document.createElement("P");
let fieldListArr;
let fieldListQuery;
document.body.appendChild(addDiv);
addDiv.appendChild(inputText);
document.body.appendChild(listDiv);
inputText.addEventListener("keypress", event => {
if (event.key === "Enter") {
fieldList = document.createElement("fieldset");
fieldDiv = document.createElement("div");
fieldDivP = document.createElement("P");
listDiv.appendChild(fieldList);
fieldList.className = "fieldListClass";
fieldList.appendChild(fieldDiv);
fieldDiv.appendChild(fieldDivP);
fieldDivP.innerHTML = inputText.value;
fieldListQuery = document.querySelectorAll(".fieldListClass") ;
}
})
fieldListQuery.forEach(element => { // <- it say fieldListQuery is undefined.
fieldListQuery.addEventListener("click", e => {
e.currentTarget.innerHTML="test";
})
});
´´´
Since I offered critique of your approach, I thought it is only fair I at least try to offer you some code that accomplishes (on the overall level, in light of absence of much detail about your solution) something along of what you have.
First off, I think creating trees of elements through a script when other solutions are more viable, tends to show an anti-pattern. Your script is invariably loaded in the context of an HTML document, which may already contain a lot of useful markup -- including an input field (that you were creating with createElement). If the input field is a "constant" there is no need to waste code on creating it -- just put it in your markup.
Second, even for elements or hierarchies of elements that are created "on demand" -- as a reaction to an event or however else -- it typically is much more readable and manageable to use templates. As a fallback -- if template cannot be used for some reason -- using innerHTML to create entire element trees is actually an appealing and more readable option than a lot of "boilerplate" containing createElement, appendChild, etc.
Third, you should always try to see if you can have your interactive controls be part of a form. I won't go into all reasons to do so, but suffice to say it helps user agents that screen-read content and for other accessibility systems, to name one. There are exceptions to this rule, but I don't recall looking at code where a control should not be part of a form -- so the rule is a good one.
Here is a proof-of-concept bare-bones to-do application:
<html>
<head>
<script>
function submit_create_todo_item_form() {
const new_todo_fragment = document.getElementById("todo-item-template").content.cloneNode(true);
new_todo_fragment.querySelector(".body").textContent = document.forms[0].elements[0].value;
document.body.appendChild(new_todo_fragment);
}
</script>
<template id="todo-item-template">
<div class="todo-item">
<p class="body"></p>
</div>
</template>
</head>
<body>
<form action="javascript: submit_create_todo_item_form()">
<input>
</form>
</body>
<html>
Take note that I use textContent instead of innerHTML to create content for a to-do item's body. innerHTML invokes the HTML parser and unless you plan to be typing hypertext into that single line of input field, innerHTML only costs you extra for no clear benefit. If you need to interpret the value verbatim, textContent is instead exactly what's needed. So, approach your solution with that in mind.
I hope this is useful, I worked with what I thought I had.

Swapping BODY content while keeping state

Dynamically swapping BODY content using jQuery html function works as expected with 'static' content.
But if forms are being used, current state of inputs is lost.
The jQuery detach function, which should keep page state, seems to be blanking out the whole page.
The following code is the initial idea using jQuery html but of course the text input value will always empty.
function swap1( ) {
$("body").html('<button onclick="swap2();">SWAP2</button><input type="text" placeholder="swap2"/>');
}
function swap2( ) {
$("body").html('<button onclick="swap1();">SWAP1</button><input type="text" placeholder="swap1"/>');
}
With not knowing what form inputs there are, how would one swap in and out these forms in the BODY and keep track of the form states?
Demo of two text inputs which should keep state when they come back into the BODY:
https://jsfiddle.net/g7hksfne/3/
Edit: missunderstood use case. This covers saving the state while manipulating the DOM, instead of switching between different states.
Instead of replacing the <body>'s content by serializing and parsing HTML, learn how to use the DOM. Only replace the parts of the DOM you actually change, so the rest of it can keep its state (which is stored in the DOM).
In your case: you might want to use something like this:
function swap1( ) {
document.querySelector("button").onclick = swap2;
document.querySelector("button").textContent = "SWAP2";
document.querySelector("input").placeholder = "swap2";
}
function swap2( ) {
document.querySelector("button").onclick = swap1;
document.querySelector("button").textContent = "SWAP1";
document.querySelector("input").placeholder = "swap1";
}
<button onclick="swap1();">SWAP1</button><input type="text" placeholder="swap1"/>
(This is not optimized and should only serve as an example.)
Put the content you want to save in a node below <body>, like a simple ´` if you don't already have a container. When you want to save and replace the container, use something like:
var saved_container = document.body.removeChild(document.querySelector("#app_container");
// or document.getElementById/getElementsByClassName, depends on container
The element is now detached and you can add your secondary to document.body. When you want to get back, save the secondary content (without overwriting the first container of course), then reattach the primary content it with:
document.body.appendChild(savedContainer);

Create a region on HTML with changing values

I am a beginner in HTML and I want to create a region on a HTML page where the values keep on changing. (For example, if the region showed "56" (integer) before, after pressing of some specific button on the page by the user, the value may change, say "60" (integer) ).
Please note that this integer is to be supplied by external JavaScript.
Efforts I have put:
I have discovered one way of doing this by using the <canvas> tag, defining a region, and then writing on the region. I learnt how to write text on canvas from http://diveintohtml5.info/canvas.html#text
To write again, clear the canvas, by using canvas.width=canvas.width and then write the text again.
My question is, Is there any other (easier) method of doing this apart from the one being mentioned here?
Thank You.
You can normally do it with a div. Here I use the button click function. You can do it with your action. I have use jquery for doing this.
$('.click').click(function() {
var tempText = your_random_value;
// replace the contents of the div with the above text
$('#content-container').html(tempText);
});
You can edit the DOM (Document Object Model) directly with JavaScript (without jQuery).
JavaScript:
var number = 1;
function IncrementNumber() {
document.getElementById('num').innerText = number;
number++;
}
HTML:
<span id="num">0</span>
<input type='button' onclick='IncrementNumber()' value='+'/>
Here is a jsfiddle with an example http://jsfiddle.net/G638z/

In XForms, with Orbeon, how to update an XML instance through jQuery?

I want to perform some logic in JavaScript when users click on the span element and update current XML instance from jQuery:
(I have seen 2 similar questions online but they never got answered!)
XForms:
<xhtml:span class="buttonPlusOne" id="plusVat">+</xhtml:span>
<xf:output ref="instance('submitted_instance')/TotVATAmnt"></xf:output>
<xf:input id="changeVatTotal" ref="instance('submitted_instance')/TotVATAmnt"></xf:input>
JavaScript:
$('span.buttonPlusOne').on('click', function () {
// do some logic and increment value for 0.01
orbeonElId = $(this).siblings('#changeVatTotal').children('input').attr('id');
// alert(orbeonElId) produces right id (changeVatTotal$xforms-input-1)
// alert(newValue) produces 0.02 (for example)
ORBEON.xforms.Document.setValue(orbeonElId, newValue);
});
I can see Orbeon posting data (Firebug), but the XML Instance does not get updated (input does not update the output - even though they share same "ref" attribute).
I suppose that you're mixing up the html element's id with the XForms id (id attribute) of the xf:input element.
The Orbeon documentation shows an example: the id to use in the ORBEON.xforms.Document.setValue() function call is the id of the (server-side) XForms element. So in your example it's changeVatTotal, and not the id of the (client-side) html input element changeVatTotal$xforms-input-1. This is why there's a request showing up in firebug with no effect on the XForms instance: the server-side XForms engine doesn't find a xf:input element with the id changeVatTotal$xforms-input-1, so it doesn't know what to do with that request.
This means, too, that you (usually) don't need to compute the id of the target element, instead you can just use the "plain" XForms id attribute value of the xf:input.
Alternatively:
Is it possible to handle the "plus" button completely in XForms? You could use a xforms:trigger control and include a xforms:setvalue action on the DOMActivate event:
<xf:trigger>
<xf:label>+</xf:label>
<xf:setvalue
ev:event="DOMActivate"
ref="instance('submitted_instance')/TotVATAmnt"
value="0.01 + instance('submitted_instance')/TotVATAmnt" />
<.... more actions ...>
</xf:trigger>
I think this solution would be more stable than doing some of the work client-side and some server-side.

What's the best way to get this data to persist within Javascript event handlers using jQuery

My code is meant to replace radio buttons with dynamic ones, and allow clicking both the label and new dynamic radio element to toggle the state of the hidden with CSS radio box.
I need to send to questions.checkAnswer() three parameters, and these are defined within these initiation loops. However I always get last the last values once the loop has finished iterating. In the past I've created dummy elements and other things that didn't feel right to store 'temporary' valuables to act as an informational hook for Javascript.
Here is what I have so far
init: function() {
// set up handlers
moduleIndex = $('input[name=module]').val();
$('#questions-form ul').each(function() {
questionIndex = $('fieldset').index($(this).parents('fieldset'));
$('li', this).each(function() {
answerIndex = $('li', $(this).parent()).index(this);
prettyRadio = $('<span class="pretty-radio">' + (answerIndex + 1) + '</span>');
radio = $('input[type=radio]', this);
radio.after(prettyRadio);
$(radio).bind('change', function() {
$('.pretty-radio', $(this).parent().parent()).removeClass('selected');
$(this).next('.pretty-radio').addClass('selected');
questions.checkAnswer(moduleIndex, questionIndex, answerIndex);
});
prettyRadio.bind('click', function() {
$('.pretty-radio', $(this).parent().parent()).removeClass('selected');
$(this).addClass('selected').prev('input').attr({checked: true});
});
$('label', this).bind('click', function() {
$(radio).trigger('change');
questions.checkAnswer(moduleIndex, questionIndex, answerIndex);
$(this).prev('input').attr({checked: true});
});
});
});
Is it bad to add a pretend attribute with Javascript, example, <li module="1" question="0" answer="6">
Should I store information in the rel attribute and concatenate it with an hyphen for example, and explode it when I need it?
How have you solved this problem?
I am open to any ideas to make my Javascript code better.
Thank you all for your time.
It's not the end of the world to add a custom attribute. In fact, in many cases, it's the least bad approach. However, if I had to do this, I would prefix the attribute the with "data-" just so that it is compliant with HTML5 specs for custom attributes for forward compatibility. This way, you won't have to worry about upgrading when you want to get HTML5 compliant.
you need to say 'var questionIndex' etc, else your 'variables' are properties of the window and have global scope...
regarding custom attributes, i have certainly done that in the past tho i try to avoid it if i can. some CMS and theming systems occasionally get unhappy if you do this with interactive elements like textareas and input tags and might just strip them out.
finally $(a,b) is the same as $(b).find(a) .. some people prefer the second form because it is more explicit in what you are doing.
If the assignment of the custom attributes is entirely client-side, you must resolve this with jQuery data, something like this:
$("#yourLiID").data({ module:1, question:0, answer:6 });
for the full documentation see here

Categories

Resources