I've built a simple practice game with 5 values stored separately. I want to put these 5 values/divs into a single object, but I'm confused about the output. the console.log returns: "the value for cardValue is [object HTMLDivElement]". The first part is working, but I'm confused about the latter. When I type into my console player.cardValue I get 'player is not defined'. Any help is appreciated. Thanks.
var cardValue = document.getElementById('cardValue');
var cardValue2 = document.getElementById('cardValue2');
var playerHit1Div = document.getElementById('playerHit1Div');
var playerHit2Div = document.getElementById('playerHit2Div');
var playerHit3Div = document.getElementById('playerHit3Div');
var player = {
cardValue: document.getElementById('cardValue'),
cardValue2: document.getElementById('cardValue2'),
playerHit1Div: document.getElementById('playerHit1Div'),
playerHit2Div: document.getElementById('playerHit2Div'),
playerHit3Div: document.getElementById('playerHit3Div')
};
for (var x in player){
console.log('the value for ' + x + ' is ' + player[x]);
}
You seem to be confused about the behaviour of the console. It's implementation dependent and has quite different behaviour in each browser that has one, so you need to learn the peculiarities of the one(s) you are using.
Running your code and entering player.cardValue into the console in IE returns:
player.cardValue
null
In Chrome it returns:
null
Which is expected as I don't have any element with an ID of cardValue. Note that if you do have suitable elements in the page, they must be before the code, otherwise when the code runs, the elements don't exist yet. Or you can run the code after the onload event (e.g. use window.onload = function(){...})
If you wish to get the text content of an element, then use its textContent property. Older IE supports innerText instead, so you can do:
var theText = element.textContent || element.innerText;
If you want to get the markup inside the element, use its innerHTML property, which has been supported by all browsers for a long time:
var theMarkup = element.innerHTML;
To see reliable output, write your own output routines and don't rely on the console's interpretation of what you might want, at least until you are familiar with how they work.
I think that console log has many cool features like error / debug / warn / info / assert, you can also profile and also trace. console.log has very cool features and is browser dependent as it was spoken in the comments.
If there is an element in DOM with id cardValue you will get in player.cardValue the Html Object that represents it, you will actually get the value or text of the DOM element depending on the kind of element it is.
For instance getting the text from an element could be some times innerText of the element and in other cases .value attribute of an input text and so on.
Accessing to those values depends on each case of the element you are trying to get info from. And not only that but actually also depends on the DOCTYPE you define, so it is good to have some kind of library to get access in the correct way to the values we need from DOM in the way we expect.
I will also add a reference a book I read, that explains that accessing with attr of jquery actually handles browser incompatibilities.
Related
Given that I need to set an element's selected index with javascript in capybara by the input name...
var element = document.querySelector("select[name='user[user_locations_attributes][0][location_attributes][state]']").selectedIndex = '50';
What is the proper way to interpret this as a string so it can be executed in Capybara with execute_script(function_name_string)? Because I keep getting syntax errors, unsure how to nest the " and ' quotations.
Easiest solution to your question is to use a heredoc
page.execute_script <<~JS
var element = document.querySelector("select[name='user[user_locations_attributes][0][location_attributes][state]']").selectedIndex = '50';
JS
Although if you have need for the element for anything else it's probably nicer to find the element in ruby and then just call execute_script on the element
el = find("select[name='user[user_locations_attributes][0][location_attributes][state]']")
el.execute_script('this.selectedIndex = 50;')
As a related question - is there a reason you're doing this via JS rather than just clicking on the correct option? If you're just scraping a page there's no issue, but if you're actually testing something this basically makes your test invalid since you could potentially be doing things a user couldn't
Since you commented that you are testing, you really shouldn't be doing this via JS, but should instead be using select or select_option. select takes the options string (which you should have - otherwise why have a select element in the first place)
select('the text of option', from: 'user[user_locations_attributes][0][location_attributes][state]')
select_option is called on the option element directly, which can be found in a number of ways, such as
find("select[name='user[user_locations_attributes][0][location_attributes][state]'] option:nth-child(50)").select_option
I'm working on localhost (so would expect to not have any domain-related probs as here).
On a page I'm using a bit of JS to modify the content of a span in the opening-window. It does not work.
When checking my code to find the control, it works (using FF dev-tools calling my Increment-function or checking the console.log-output): $('#uploads_Count')returns an object of type HTMLSpanElement. However, trying to access the same control from an opened window's console with window.opener.$('#uploads_Count'), this returns an HTML-Document, seemingly the entire page. Why is this not working, what am I missing here?
Here is function that is supposed to increment the counter contained in the span whose id is given as argument:
function Increment(ctrl)
{
var gef = $("#" + ctrl);
if (!gef) // did not find control, maybe on opener?
{
gef = window.opener.$("#" + ctrl);
}
console.log(gef);
cnt = parseInt(gef.text() , 10);
cnt++;
gef.text(cnt);
}
The HTML is trivial:
<span id="uploads_Count">0</span>
If $(selector) returns an element (such as HTMLSpanElement), rather than a collection of elements (would look like [<span id="uploads_Count"></span>] in most dev tools), then you're not calling jQuery.
Dev tools in A-grade browsers tend to introduce $ as a selector function. It is available in the developer console only.
If window.jQuery exists, then it's likely that jQuery.noConflict() was called, in which case you should use window.opener.jQuery.
Found it!
The way I checked if the control was found, was wrong. Instead of if (!gef)I should have used if (!gef.length). Found the explanation here.
I have a jsfiddle that demonstrates the question:
http://jsfiddle.net/H6gML/8/
$(document).ready(function() {
// this seems fine in IE9 and 10
var $div = $("<div>");
console.log("In IE, this <div> is just fine: " + $div[0].outerHTML);
// this is weird in IE
var $test = $("<test>");
console.log("However, this <test> has an xml tag prepended: \n"
+ $test[0].outerHTML);
$test.find("test");
console.log("Now, it does not: \n" + $test[0].outerHTML);
console.log("Why does this behave this way?");
});
Why does this happen? It doesn't happen in Chrome or Firefox. Is there a better way to fix this than to call .find("test") on the object?
Edit
To clarify, I'm not asking why the xml tag is added, rather, I'm wondering why the .find() call get's rid of it. It doesn't make sense to me.
Why does this happen? It doesn't happen in Chrome or Firefox. Is there a better way to fix this than to call .find("test") on the object
It is the IE causing the issue while doing document.createElement on an unknown html element type. It thinks it is an XML node and adds the xml namespace prefixed <?XML:NAMESPACE PREFIX = PUBLIC NS = "URN:COMPONENT" />. Instead if you try to make it explicit to mention that it is an html element, this issue doesn't happen.
Try:
var $test = $("<html><test/></html>");
The issue no longer occurs.
To clarify, I'm not asking why the xml tag is added, rather, I'm wondering why the .find() call get's rid of it. It doesn't make sense to me.
Now, when you do a find, jquery internally uses context.getElementsByTagName or (similar based on the type whether it is a class or a tag or id etc..) which means it does this operation on the element test. So in IE when you do that it probably internally resolves the fact that you are trying to perform the operation on an html element and not an xml element and it changes the document type for the underlying context(But i don't know why it changes the parent context though rather than just returning a match). You can check this out by this simple example as well.
var $test = document.createElement("test");
console.log("However, this <test> has an xml tag prepended: \n"
+ $test.outerHTML);
$test.getElementsByTagName("test");
console.log("Now, it does not: \n" + $test.outerHTML);
Demo
Update
Here is a documented way of defining the custom elements
The custom element type identifies a custom element interface and is a sequence of characters that must match the NCName production and contain a U+002D HYPHEN-MINUS character. The custom element type must not be one of the following values:
annotation-xml,
color-profile,
font-face,
font-face-src,
font-face-uri,
font-face-format,
font-face-name,
missing-glyph
So according to this had your tag name been somename-test ex:- custom-test IE recognizes it and it works as expected.
Demo
So, I have some code that should do four things:
remove the ".mp4" extension from every title
change my video category
put the same description in all of the videos
put the same keywords in all of the videos
Note: All of this would be done on the YouTube upload page. I'm using Greasemonkey in Mozilla Firefox.
I wrote this, but my question is: how do I change the HTML title in the actual HTML page to the new title (which is a Javascript variable)?
This is my code:
function remove_mp4()
{
var title = document.getElementsByName("title").value;
var new_title = title.replace(title.match(".mp4"), "");
}
function add_description()
{
var description = document.getElementsByName("description").value;
var new_description = "Subscribe."
}
function add_keywords()
{
var keywords = document.getElementsByName("keywords").value;
var new_keywords = prompt("Enter keywords.", "");
}
function change_category()
{
var category = document.getElementsByName("category").value;
var new_category = "<option value="27">Education</option>"
}
remove_mp4();
add_description();
add_keywords();
change_category();
Note: If you see any mistakes in the JavaScript code, please let me know.
Note 2: If you wonder why I stored the current HTML values in variables, that's because I think I will have to use them in order to replace HTML values (I may be wrong).
A lot of things have been covered already, but still i would like to remind you that if you are looking for cross browser compatibility innerHTML won't be enough, as you may need innerText too or textContent to tackle some old versions of IE or even using some other way to modify the content of an element.
As a side note innerHTML is considered from a great majority of people as deprecated though some others still use it. (i'm not here to debate about is it good or not to use it but this is just a little remark for you to checkabout)
Regarding remarks, i would suggest minimizing the number of functions you create by creating some more generic versions for editing or adding purposes, eg you could do the following :
/*
* #param $affectedElements the collection of elements to be changed
* #param $attribute here means the attribute to be added to each of those elements
* #param $attributeValue the value of that attribute
*/
function add($affectedElements, $attribute, $attributeValue){
for(int i=0; i<$affectedElements.length; i++){
($affectedElements[i]).setAttribute($attribute, $attributeValue);
}
}
If you use a global function to do the work for you, not only your coce is gonna be easier to maintain but also you'll avoid fetching for elements in the DOM many many times, which will considerably make your script run faster. For example, in your previous code you fetch the DOM for a set of specific elements before you can add a value to them, in other words everytime your function is executed you'll have to go through the whole DOM to retrieve your elements, while if you just fetch your elements once then store in a var and just pass them to a function that's focusing on adding or changing only, you're clearly avoiding some repetitive tasks to be done.
Concerning the last function i think code is still incomplete, but i would suggest you use the built in methods for manipulating HTMLOption stuff, if i remember well, using plain JavaScript you'll find yourself typing this :
var category = document.getElem.... . options[put-index-here];
//JavaScript also lets you create <option> elements with the Option() constructor
Anyway, my point is that you would better use JavaScript's available methods to do the work instead of relying on innerHTML fpr anything you may need, i know innerHTML is the simplest and fastest way to get your work done, but if i can say it's like if you built a whole HTML page using and tags only instead of using various semantic tags that would help make everything clearer.
As a last point for future use, if you're interested by jQuery, this will give you a different way to manipulate your DOM through CSS selectors in a much more advanced way than plain JavaScript can do.
you can check out this link too :
replacement for innerHTML
I assume that your question is only about the title changing, and not about the rest; also, I assume you mean changing all elements in the document that have "title" as name attribute, and not the document title.
In that case, you could indeed use document.getElementsByName("title").
To handle the name="title" elements, you could do:
titleElems=document.getElementsByName("title");
for(i=0;i<titleElems.length;i++){
titleInner=titleElems[i].innerHTML;
titleElems[i].innerHTML=titleInner.replace(titleInner.match(".mp4"), "");
}
For the name="description" element, use this: (assuming there's only one name="description" element on the page, or you want the first one)
document.getElementsByName("description")[0].value="Subscribe.";
I wasn't really sure about the keywords (I haven't got a YouTube page in front of me right now), so this assumes it's a text field/area just like the description:
document.getElementsByName("keywords")[0].value=prompt("Please enter keywords:","");
Again, based on your question which just sets the .value of the category thingy:
document.getElementsByName("description")[0].value="<option value='27'>Education</option>";
At the last one, though, note that I changed the "27" into '27': you can't put double quotes inside a double-quoted string assuming they're handled just like any other character :)
Did this help a little more? :)
Sry, but your question is not quite clear. What exactly is your HTML title that you are referring to?
If it's an element that you wish to modify, use this :
element.setAttribute('title', 'new-title-here');
If you want to modify the window title (shown in the browser tab), you can do the following :
document.title = "the new title";
You've reading elements from .value property, so you should write back it too:
document.getElementsByName("title").value = new_title
If you are refering to changing text content in an element called title try using innerHTML
var title = document.getElementsByName("title").value;
document.getElementsByName("title").innerHTML = title.replace(title.match(".mp4"), "");
source: https://developer.mozilla.org/en-US/docs/DOM/element.innerHTML
The <title> element is an invisible one, it is only displayed indirectly - in the window or tab title. This means that you want to change whatever is displayed in the window/tab title and not the HTML code itself. You can do this by changing the document.title property:
function remove_mp4()
{
document.title = document.title.replace(title.match(".mp4"), "");
}
I'm getting an xml file and want to get data from it.
The source of the xml doesn't really matter but what I;ve got to get a certain field is:
tracks = xmlDoc.getElementsByTagName("track");
variable = tracks.item(i).childNodes.item(4).childNodes.item(0).nodeValue;
Now this works like a charm, EXCEPT when there is no value in the node. So if the structure is like this:
<xml>
<one>
<two>nodeValue</two>
</one>
<one>
<two></two>
</one>
</xml>
the widget will crash on the second 'one' node, because there is no value in the 'two' node. The console says:
TypeError: tracks.item(i).childNodes.item(4).childNodes.item(0) has no properties
Any ideas on how to get the widget to just see empty as an empty string (null, empty, or ""), instead of crashing? I'm guessing something along the lines of data, getValue(), text, or something else.
using
var track= xmlDoc.getElementsByTagName('track')[0];
var info= track.getElementsByTagName('artist')[0];
var value= info.firstChild? info.firstChild.data : '';
doesn't work and returns "TypeError: track has no properties". That's from the second line where artist is called.
Test that the ‘two’ node has a child node before accessing its data.
childNodes.item(i) (or the JavaScript simple form childNodes[i]) should generally be avoided, it's a bit fragile relying on whitespace text nodes being in the exact expected place.
I'd do something like:
var tracks= xmlDoc.getElementsByTagName('track')[0];
var track= tracks.getElementsByTagName('one')[0];
var info= track.getElementsByTagName('two')[0];
var value= info.firstChild? info.firstChild.data : '';
(If you don't know the tagnames of ‘one’ and ‘two’ in advance, you could always use ‘getElementsByTagName('*')’ to get all elements, as long as you don't need to support IE5, where this doesn't work.)
An alternative to the last line is to use a method to read all the text inside the node, including any of its child nodes. This doesn't matter if the node only ever contains at most one Text node, but can be useful if the tree can get denormalised or contain EntityReferences or nested elements. Historically one had to write a recurse method to get this information, but these days most browsers support the DOM Level 3 textContent property and/or IE's innerText extension:
var value= info.textContent!==undefined? info.textContent : info.innerText;
without a dtd that allows a one element to contain an empty two element, you will have to parse and fiddle the text of your xml to get a document out of it.
Empty elements are like null values in databases- put in something, a "Nothing" or "0" value, a non breaking space, anything at all- or don't include the two element.
Maybe it could be an attribute of one, instead of an element in its own right.
Attributes can have empty strings for values. Better than phantom elements .
Yahoo! Widgets does not implement all basic javascript functions needed to be able to use browser-code in a widget.
instead of using:
tracks = xmlDoc.getElementsByTagName("track");
variable = tracks.item(i).childNodes.item(4).childNodes.item(0).nodeValue;
to get values it's better to use Xpath with a direct conversion to string. When a string is empty in Yahoo! Widgets it doesn't give any faults, but returns the 'empty'. innerText and textContent (the basic javascript way in browsers, used alongside things like getElementsByTagName) are not fully (or not at all) implemented in the Yahoo! Widgets Engine and make it run slower and quite awfully react to xmlNodes and childNodes. an easy way however to traverse an xml Document structure is using 'evaluate' to get everything you need (including lists of nodes) from the xml.
After finding this out, my solution was to make everything a lot easier and less sensitive to faults and errors. Also I chose to put the objects in an array to make working with them easier.
var entries = xmlDoc.evaluate("lfm/recenttracks/track");
var length = entries.length;
for(var i = 0; i < length; i++) {
var entry = entries.item(i);
var obj = {
artist: entry.evaluate("string(artist)"),
name: entry.evaluate("string(name)"),
url: entry.evaluate("string(url)"),
image: entry.evaluate("string(image[#size='medium'])")
};
posts[i] = obj;
}