so I'm working on recreating the card game Dominion in Javascript. I'm using OO Javascript, creating a card object for each card.
Rather than posting all my code here I'll save some space and link you to it:
Site:
http://people.rit.edu/lxl1500/Prog4/Project%201/project1.html
Script:
http://people.rit.edu/lxl1500/Prog4/Project%201/main-script.js
Where I'm running into issues:
In my createActions() function I'm adding an onclick event to each image (I'm creating the images by grabbing the smallImage property of each object). This onclick should call my fullImage() function. This function will simply show a larger version of the card so the player can see the details. Therefore, I want to pass the image property, which holds the string for the source of the larger image to the function.
fullImage() will accept the object property as imageSrc. You'll see now by clicking on an image you'll get an alert of undefined. The only thing I can think of is when I'm calling fullImage(this.image) - this is reference to the image itself rather than the object...I'm not sure how to accomplish what I'm trying to do though. Any help would be greatly appreciated!
Thank you for your time.
You're setting onclick as an attribute. You might be able to work with that somehow, but it's a much more elegant solution to attach an event listener:
card.addEventListener('click', function() {
// Handle click.
}, false);
Then you can use actionCards[i] as normal... except you can't, because i will be one over the last index, for reasons I won't get into here.1 The easiest way to fix that is to bind the function before you add it as an event listener:
card.addEventListener('click', function() {
// Handle click.
}.bind(actionCards[i]), false);
(I should note that bind is not built-in in some older browsers. While I'm on the topic of browser compatibility, addEventListener isn't supported by old versions of Internet Explorer, either.) Then you can use this to refer to your Card object.
Footnotes
1 Okay, fine. It's because of JavaScript's function scope and that when the user has clicked it, the loop has finished, and when the loop has finished, i will be one over the number of cards.
not sure why you have this.image. image isn't a property of an image element. remember a onClick event handler will be called with this scope set to the element the onclick was happen. change it to just fullimage(this) and you'll see the img element is passed in, you could then change your fullimage method to imageSrc.src = 'some url'; to change the image src attribute.
Change fullImage(this.image)
to fullImage(this.src) to get the source.
on modern browsers do this:
card.addEventListener('click', fullImage.bind(actionCards[i]), false);
Yes, you're correct that the this.src reference isn't pointing to what you want it. When the Image card gets clicked, it runs the javascript
fullImage(this.image)
Since this javascript isn't run with any connection to the img that called it, Javascript won't know which image called it. One fix to this is hard-coding the correct src url into the function call when you set the properties for the card, so this:
card.setAttribute("onClick","fullImage(this.image)");
becomes this:
card.setAttribute("onClick","fullImage('" + card.src + "')");
This way the card's src is resolved before the function call is made, and when the function is called it knows which src to echo.
Hope this helps!
edited to include quotes
Related
If I dynamically create an element and then add an onclick="function()" to it, why doesn't that code get physically added to the element, the same way adding a class to it would?
Asking because I'm trying to pass a board game back and forth between players. I can send the innerHTML of the container element via AJAX to the next player and the physical game will load for them - but then any eventhandlers on dynamically created pieces are no longer there.
Do I have to create a function to put them back? I don't think that would be too difficult, but if there was a way to get the onclick code to be written to the html page when added to the elements so that it passes in the innerHTML that would be much easier.
You could pass around the innerHTML via AJAX as you are currently, but using onclick is vulnerable to XSS attacks, unless you do some sort of JS obfuscation.
Easier than obfuscation would be to share common Javascript for both players with click handlers. Like this:
// Assign an ID for every button, and handle each event separately.
const buttonElement = document.querySelector('#btnOne');
// This is the equivalent of `onclick`
buttonElement.addEventListener('click', function (event) {
alert('Element clicked through function!');
});
dynamically create an element, add event needs to use event delegation.
I want to do the following:
<div id="theDiv" style="width: aJavascriptVariableOrFunctionCallToGetValue">TESING</div>
I don't want to use, elsewhere in the code,
document.getElementById('theDiv').style.width = someValue;
I actually want that div, when it first appears, to have a width set, inline, by either a JavaScript variable or by way of a call to a JavaScript function.
How can I do this?
This is impossible to do the way you see this.
Every time the variable changes, you need to update the style of that particular object:
var theDiv = document.getElementById("theDiv");
document.getElementById('theDiv').style.width = someValue;
I really don't understand what you mean that when it first appears you want it's width to be set to certain width - why do you want to do that inline? Why can't you just set the width in your Javascript? What's preventing you from doing that? Especially if you want to do it just once and don't want to change it dynamically.
If you want to link the width of the div to a variable, look at frameworks like Backbone or EmberJS. You can then define a renderer that changes the width when the variable changes.
The only way to get JavaScript to run when an element first appears is with an onload event handler. And onload events only work on a few specific elements, like body, script or img.
Here is how you could make it work in your case, with a img tag:
<div id="theDiv">
TESING
<img style="display:none;" src="tinyImage.jpg" onload="this.parentNode.style.width='100px';"/>
</div>
Honestly, I don't see this as a good practice, and I would recommend to just be patient, and set the width later in a script.
Live demo: http://jsfiddle.net/HKW6b/
You cannot do it like that. There are other ways to achieve what you want, though.
Server side processing, specially if the technology you use supports templating. You can manipulate the html value before sending it to the client;
jQuery may be something to consider. Simply fetch the element and use its API. Example:
$("#theDiv").width(aJavascriptVariableOrFunctionCallToGetValue);
This small piece of code does exactly what you want. It is not written inside the element itself, and it is about equivalent to the sample you provided, but again, it's something to consider should you have to do more complex operations on the DOM later on.
If you want to execute that piece of code only once, and after the page is ready, you can do it like this:
var div = $("#theDiv");
div.ready(function () {
div.width(aJavascriptVariableOrFunctionCallToGetValue);
});
The solution for this issue allowed me to set the proper width of the div immediately, asymptotically approaching inlined-javascript as possible, as per one of the comments above suggested:
"If you need the style applied immediately, you can embed a script immediately following your HTML markup and then you won't have the flash of unstyled content I'm guessing you want to avoid. – Harvey A. Ramer"
This solved the 'slow server' => FOUC problem. I added a 'script' tag immediately after the div tag to set the div to the window.innerWidth, problem solved.
From what I've seen, this approach is the earliest/soonest/fastest way to use javascript to set a CSS style attribute -- and it avoids having to code up an 'onload' handler.
The problem with 'onload' handlers being used to set UI style attributes on the page is -- the onload Javascript handler function can grow...and grow...and grow over time over the project's lifespan and you eventually are forced to clean out the onload handler. Best approach is to never use an onload handler that sets styles in the first place.
I'm using React, and this syntax worked for me:
<div id="theDiv" style={`width: ${aJavascriptVariable} %`}>TESING'</div>
I am attempting to fire off an AJAX call based on the onclick event for a google map integration. The info_window_content function seen here: http://jsfiddle.net/6xw2y/ is the call to create the divs that reside within the map itself.
The "v" variable does in fact contain a store_id. So in the opening line of that function, it has the following:
var info_window_string = "<div class='maps_popup' id="+v.id+">";
Now I have an onclick event that I have duplicated and modified. The first onclick event works just fine and refreshes the panel as it should. The second onclick event doesn't work and the code for that is below:
$("#div").click(function(){
var store_id = $(this).find("div").attr("id");
var pathname = "ajax=1&store_id="+store_id+"&action=get_nearby_stores&distance="+distance+"&lat="+lat+"&lng="+lng+"&products="+$('#edit-products').val();
$("#pocp_content").load("file1.php?" + pathname);
});
That doesn't seem to work. I've also tried changing the div tag to be like this:
$("div").click(function(){
Which still doesn't work. As an added side hint. At one point I was able to get it to refresh but it was passing map-container as the store_id, instead of the id itself.
What am I missing here?
I agree with Joke_Sense10,
but I think you're probably not binding the event to the right DOM element.
Try to open up the developer console in your browser (while being on the side you develop this code for), and enter $("#div") to see if the element it returns is the one you expect. You can also use console.log($("#div")) in the code for that.
answer in comments
For a larger number of elements, always use .on() method as the latter will bind an single event listener on one of the topmost nodes in the DOM tree.
$(document).on("click","#"+v.id, function(){
I have data-segment attribute in my body tag that is changed by a slider. I need to trigger a function based on the value of this, when it is changed.
I'm not sure how to attach an event listener in this case though?
`There is no reliable cross-browser way to receive an event when a DOM node attribute is changed. Some browsers support "DOM mutation events", but you shouldn't rely on them, and you may Google that phrase to learn about the ill-fated history of that technology.
If the slider control does not fire a custom event (I would think most modern ones do), then your best bet is to set up a setInterval() method to poll the value.
You can use MutationObserver, which is an API available in every browser and avoid polling with setInterval. If you have a reference to your element in element, you could do this:
const mutationObserver = new MutationObserver(callback);
mutationObserver.observe(element, { attributes: true });
function callback() {
// This function will be called every time attributes are
// changed, including `data-` attributes.
}
Other changes in your element can be easily detected with this API, and you can even get the old and new values of the property that changes in your callback tweaking the configuration object in the call to observe. All the documentation about this can be read in MDN: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
Since in my use case all of my .data('type', 'value') is set inside javascript block anyway, so in my case I just put a .change() chain right after the .data(...) and access the update using the normal $("#oh-my-data").change()
Which works fine for me, see the demo.
$("#oh-my-data").change(function() {
$("#result").text($("#result").text() + ' ' + $(this).data('value'));
})
$("#oh-my-data").data('value', 'something1').change();
$("#oh-my-data").data('value', 'something2').change();
$("#oh-my-data").data('value', 'something3').change();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="oh-my-data" type="hidden" data-value="" /> Result: <span id="result"></span>
But this solution have a problem, which is that if the .data() is set by an external library, then this will not work since you can't add the .change() on top of it.
I'm trying to write a Firefox extension that adds elements to the loaded page. So far, I get the root element of the document via
var domBody = content.document.getElementsByTagName("BODY").item(0);
and create the new elements via
var newDiv = content.document.createElement("div");
and everything worked quite well, actually. But the problems came when I added a button with on onclick attribute. While the button is correctly displayed, I get an error. I already asked asked here, and the answer with document.createElement() (without content) works.
But if I remove the 'content.' everywhere, the real trouble starts. Firstly, domBody is null/undefined, no matter how I try to access it, e.g. document.body (And actually I add all elements _after_the document is fully loaded. At least I think so). And secondly, all other elements look differently. It's seem the style information, e.g., element.style.width="300px" are no longer considered.
In short, with 'content.document' everything looks good, but the button.onclick throws an error. with only 'document' the button works, but the elements are no longer correctly displayed. Does anybody see a solution for that.
It should work fine if you use addEventListener [MDN] (at least this is what I used). I read somewhere (I will search for it) that you cannot attach event listener via properties when creating elements in chrome code.
You still should use content.document.createElement though:
Page = function(...) {
...
};
Page.prototype = {
...
addButton : function() {
var b = content.document.createElement('button');
b.addEventListener('click', function() {
alert('OnClick');
}, false);
},
...
};
I would store a reference to content.document somewhere btw.
The existing answer doesn't have a real explanation and there are too many comments already, so I'll add another answer. When you access the content document then you are not accessing it directly - for security reasons you access it through a wrapper that exposes only actual DOM methods/properties and hides anything that the page's JavaScript might have added. This has the side-effect that properties like onclick won't work (this is actually the first point in the list of limitations of XPCNativeWrapper). You should use addEventListener instead. This has the additional advantage that more than one event listener can coexist, e.g. the web page won't remove your event listener by setting onclick itself.
Side-note: your script executes in the browser window, so document is the XUL document containing the browser's user interface. There is no <body> element because XUL documents don't have one. And adding a button won't affect the page in the selected tab, only mess up the browser's user interface. The global variable content refers to the window object of the currently selected tab so that's your entry point if you want to work with it.