I'm working for a friend that needs to manipulate an interactive PDF with the following needs:
there are 2 main "layers" and 2 buttons. When I press button1 only the layer1 is visible, when I press button2, the layer2 is the only visible.
He can upload a javascript for each button with the action that must start on the click, and this is the code I wrote for him.
var layerToHideLabel = "testlayer1";
var layerToShowLabel = "testlayer2";
showField(layerToShowLabel);
hideField(layerToHideLabel);
function showField(fieldName){
var field = null;
field = FindOCG(fieldName);
if(field != null){
setVisibility(field,true);
}
}
function hideField(fieldName){
var field = null;
field = FindOCG(fieldName);
if(field != null){
setVisibility(field,false);
}
}
function setVisibility(field, status){
field.state=status;
}
function FindOCG(cName) {
var aOCGs = this.getOCGs();
for(var i=0; aOCGs && i<aOCGs.length;i++) {
if(aOCGs[i].name == cName) return aOCGs[i];
}
return null;
}
The getOCGs function is a built-in functionality for the document, and of course, when I launch the method I get the following error:
this.getOCGs is not a function
I think the issue is context related, as the script is launched on the click of the button, so maybe this refers to the button and not to the document.
I tried lot of combinations to retrieve the document from the OCG, with no luck. I tried document, $doc, doc, Document, but nothing. I'm afraid it's not even possible, maybe I should try to upload some code at document level, but I don't know if I can add some onchange functionality.
This is the guide I've been following so far:
https://acrobatusers.com/tutorials/create_use_layers
Thanks in advance
Instead of 'this.getOCGs()', try calling 'event.target.doc.getOCGs()'.
In a function defined in one of the folder-level JavaScripts files, 'this' is undefined. Calling functions should pass the Doc to any function at this level that needs it.
The Doc object can often be accessed through event objects, which are created for each event by which a JavaScript is executed:
● For mouse, focus, blur, calculate, validate, and format events, event.target returns the Field object that initiated the event. You can then access the Doc object through the Doc method of the Field object.
Related
I appreciate how browsers work when deciding whether a window.open() was triggered by a click, as in, its only allowed when it came from a real element click event.
I want to write the same logic myself, how do I do that?
Say I have any JS function, called potentially anywhere and everywhere from the rest of my application, sometimes with a click event handler at the start of the callstack and some times not. How can I know this inside my method, without explicitly passing information about the start of the stack (click vs not) all the way around my application?
function iNeedToKnowIfStackFrame0WasAClickEventListener() {
var wasAClick = ???;
if(wasAClick)
window.open(...);
else
something.else();
}
You can get the event information using this.event.type.
You can either pass the event into the function like so:
function iNeedToKnowIfStackFrame0WasAClickEventListener(event) {
var wasAClick = event.type == "click";
if(wasAClick)
window.open(...);
else
something.else();
}
or you can get the event off this
function iNeedToKnowIfStackFrame0WasAClickEventListener() {
var wasAClick = this.event.type == "click";
if(wasAClick)
window.open(...);
else
something.else();
}
I defined a custom function in the header section that checks, alerts the user, and resets the value of a particular slider bar when it fails certain restrictions.
This function works beautifully when called on question clicks:
this.questionclick = chkVals;
I would like to also run the function when the user are exiting the text input field (as some users are using the keyboard to do the survey). I implemented an Event Listener for each sliders' text input field that runs the function when the focus is out of the text input field.
// choices is an array of choice ids
for (i = 0; i < choices.length; i++) {
var x = document.getElementById(choices[i]);
x.addEventListener("blur", chkVals, true);
};
I know that the event listener works, because the correct alerts are popping up. It is just not able to reset the values as this.setChoiceValue is not a function within the environment. I have tried setting var that = this; and calling that.setChoiceValue in the function, but it still does not work.
Any help will be greatly appreciated!
You haven't shown all your code, so I'm making some assumptions.
this is the Qualtrics question object in the addOnload function. Since chkVals is outside the addOnload function, this (or that) is undefined. So, you need to pass it in your function call (function chkVals(qobj)) then use qobj.setChoiceValue in the chkVals function. Then your function calls become:
this.questionClick = chkVals(this);
and
x.addEventListener("blur", chkVals(this), true);
#T. Gibbons 's answer helped me get to this point. As suggested I needed to add a parameter to chkVals() to be able to reference the this object. However,
this.questionClick = chkVals(this);
does not work due to this being a reserved object, so the whole header script will not run. I ended up changing all reference of this to that in my custom function and adding the parameter that as suggested:
function chkVals(that) {
...
... that.setChoiceValue(x, y)
}
To call the function with a parameter, I had to explicitly defined an anonymous function that called chkVals, otherwise it will not work (I am not sure why):
var that = this;
this.questionclick = function() {chkVals(that);}
for (i = 0; i < choices.length; i++) {
var x = document.getElementById(choices[i]);
x.addEventListener("blur", function() {chkVals(that);}, true);
};
The above works!
I'm developing a Chrome extension, and I'm adding an onmouseover handler to each of the images on a page. When the user mouses over an image, it's URL should be stored in a variable. I know I can easily get the value of the src attribute of the image, but I want the full URL. The src attribute stores the path of the image on the server. For example, when you right click an image in Google Chrome, you get the "Copy Image URL" option, which copies the image's URL to the clipboard.
Is there any way to achieve this? Thanks.
Instead of imageElement.getAttribute("src") or $("img.something").attr("src"), which reads the original markup, use imageElement.src property which will always give you the full URL.
var imgFullURL = document.querySelector('img.something').src;
or:
var imgFullURL = $('img.something')[0].src;
To extract host name, path name etc. - parse the url with URL() constructor, which works in modern browsers or use the legacy method via creating a temporary a node.
You can use window.location to get the page you are currently on and the following will give you the URL parts you need:
window.location.protocol = "http:"
window.location.host = "stackoverflow.com"
window.location.pathname = "/questions/32828681/how-to-get-url-of-an-image-in-javascript"
So, likely, you will need protocol, then "//", then host and finally the image src.
So the TL;DR is this:
(function() {
const imageInfo = new Object();
imageInfo.source = '';
window.addEventListener('mouseover', function (event) {
var currentElement = event.target;
// console.log(event.target);
if (currentElement.tagName === 'IMG') {
// console.log(currentElement.outerHTML + "is a photo");
imageInfo.source = currentElement.src;
// console.log("src is :" + imageInfo.source)
return imageInfo.source;
}
})
})();
See CodePen:
How to find the src URL for a photo by Trevor Rapp on
CodePen
This is how I thought about solving the problem in the most basic steps:
get the function to fire.
get the function to add an event listener that will perform an action on a mouseover event.
make that action know what the mouse is currently over.
figure out if what the mouse is currently over is an image or not.
create logic that will respond if it is.
that action that logic should do is return the source URL.
I will need to store that source URL if I am going to have to return it.
Here are how each of those solutions looked:
get the function to fire.
An IFFE is a great way to get a function to fire without having to worry about polluting the name space.
//skeleton for an IFFE statement
(function() {
})();
get the function to add an event listener that will perform an action on a mouseover event.
An event listener that could fire anywhere would have to be attached to the window or the document.
make that action know what the mouse is currently over.
This part will be combined with part 2. Event listener's first parameter is what type of event you want to listen for -- in this case 'mouseover. So now our code looks like this
(function () {
window.addEventListener('mouseover', function (event) {
//do stuff here
}
})()
figure out if what the mouse is currently over is an image or not.
*To figure out which element the mouse if currently over you would use Event.target.
The MDN definition for that is: *
The target property of the Event interface is a reference to the object onto which the event was dispatched. It is different from Event.currentTarget when the event handler is called during the bubbling or capturing phase of the event. --Event.Target
*So the code would then look like this: *
(function () {
window.addEventListener('mouseover', function (event) {
//get the current element the mouse is over
var currentElement = event.target;
}
})()
create logic that will respond if it is.
This was a little trickier since a photo or IMG can be presented in various ways.
I chose to create a solution for the simplest way, which is assuming that the web developer used the more syntactically correct version of an tag. However, there are many times when they may choose to apply a 'background-image' CSS property to a normal . Other things to consider could be the use of iframes, which can make detecting the attributes of child elements very frustrating since they don't allow bubbling to occur. To tell if an element is an , you can simply use elem.tagName === "IMG" for your logic check. While not included in the above code, if you wanted to check if a div is using the 'background-image', you could use something like element.getAttribute('style').includes('term') and switch out 'term' for something like 'url' or 'jpg' or 'png.' Kind of clunky and hacky, but just a thought. Anyway, the code would then become
(function () {
window.addEventListener('mouseover', function (event) {
//get the current element the mouse is over
var currentElement = event.target;
if (currentElement.tagName === 'IMG') {
//do stuff
}
}
})()
that action that logic should do is return the source URL.
Once you get the logic done and you have properly selected the element, then you can use element.src to get the source URL.
I will need to store that source URL if I am going to have to return it.
You can do this anyway you want, but I played around with instantiating an object since it sounded like the value would need to change often, but you didn't necessarily need to store previous values.
And so the final product could be something like this
(function() {
const imageInfo = new Object();
imageInfo.source = '';
window.addEventListener('mouseover', function (event) {
var currentElement = event.target;
// console.log(event.target);
if (currentElement.tagName === 'IMG') {
// console.log(currentElement.outerHTML + "is a photo");
imageInfo.source = currentElement.src;
// console.log("src is :" + imageInfo.source)
return imageInfo.source;
}
})
})();
I have a function, which at the end of its task assigns a button to a new id.
function altChecker() {
var doc = document,
out = [],
appButton = doc.getElementById('appButton'),
//re = /click-me/gi,
output = doc.createElement('p');
output.setAttribute('id', 'output');
EventUtility.addHandler(appButton, 'click', function(e) {
//I have not included all the function details to concentrate on the question
appButton.id = 'appButtonNextChecker';
var appButtonNextChecker = doc.getElementById('appButtonNextChecker');
nextChecker(appButtonNextChecker);
});
}
function nextChecker(newBtnName) {
EventUtility.addHandler(newBtnName, 'click', function(e) {
$('#output').innerHTML = "";
console.log('next Checker, button!')
});
}
So basically there is one button in the DOM assigned to appButton ID initially, and then I change it doing:
appButton.id = 'appButtonNextChecker';
when the altChecker function fires...
Then I assign the button to a new variable, and pass in the variable to the next function...
var appButtonNextChecker = doc.getElementById('appButtonNextChecker');
nextChecker(appButtonNextChecker);
While I can see the buttons' ID change in the DOM, and I see the console.log fire in the nextChecker function,
$('#output').innerHTML = ""; //doesn't fire
AND the altChecker function fires as well (again)?! Haven't I severed the connection to the click function when I reassigned the new ID?
Any help would be appreciated!
Javascript doesn't remember that you initially attached the event through it's id. The event is attached to the element itself, not the ID. It's not like CSS that way.
In fact your variables are still holding the same element as well, so there's no need to create a new variable after changing the ID, either. Since you're using jQuery you can just type $(appButton).unbind(); to remove the event handler. You may also want to look into .on() and .off()
The problem is that you're trying to use the innerHTML property in a jQuery's object.
That property belongs to Element, and it will not work in the way you're using it.
You can use the document.getElementById method, and it will work fine:
document.getElementById('output').innerHTML = '';
Or you can use jQuery's html method:
$('#output').html('');
And you can even use the first element of the jQuery's array, and use innerHTML again:
$('#output')[0].innerHTML = '';
It's up to you, but the first option will be faster, for sure.
Let's say I have a page that loads pages dynamically. As each page loads into the DOM, events for the elements in that page are added.
If the user loads another page, the elements loaded previously will be removed from the DOM. Naturally, because the elements themselves no longer exist, any events mapped to those elements cease to function.
However, are they also removed? Or are they sitting in the user's memory, taking up space?
Follow-up:
Were a function defined as such:
var event = $('foobar').addEvent('click', function() {
alert(1);
});
One could easily remove the event with event = null (or so I'd assume!)...
but what if the event were not saved to a local variable?
$('foobar').addEvent('click', function() {
alert(1);
});
Thanks!
first of all. what? this makes no sense:
var event = $('foobar').addEvent('click', function() {
alert(1);
});
it does not save the event into a local variable as you seem to think. it saves a reference to the foobar element object into the event variable - most mootools element methods will return this for chaining, which is the element itself and not the result of the method (unless it's a getter like '.getStyle').
it then depends on how you get rid of the element what happens next. first off, element.destroy, found here: https://github.com/mootools/mootools-core/blob/master/Source/Element/Element.js#L728
it will remove the element from the dom and from memory, and empty it in a safe way. it will be reliant on the browser's GC to clean up once it's gone, mootools won't do any spectacular GC for you for the element itself but it does run the special clean function on the child nodes as well: var children = clean(this).getElementsByTagName('*');.
the clean method also gets rid of any event handlers and storage attached to the child elements of the div.
THEN. events added by mootools go into element storage. Element storage is in an object behind a closure which the element proto uses. To test it, we will re-implement it and make it puncturable (a global object called storage) so we can check what happens to the reference after the parent is gone:
http://jsfiddle.net/dimitar/DQ8JU/
(function() {
var storage = this.storage = {}; // make it puncturable
var get = function(uid){
return (storage[uid] || (storage[uid] = {}));
};
Element.implement({
retrieve: function(property, dflt){
var storage = get($uid(this)), prop = storage[property];
if (dflt != null && prop == null) prop = storage[property] = dflt;
return prop != null ? prop : null;
},
store: function(property, value){
var storage = get($uid(this));
storage[property] = value;
return this;
},
eliminate: function(property){
var storage = get($uid(this));
delete storage[property];
return this;
}
});
})();
// read it.
var link = document.getElement("a");
var uid = link.uid; // will reference the mootools unique id for it
// add an event handler
link.addEvent("click", function(e) {
console.log("hi");
this.destroy();
// see what's left in storage for that element.
console.log(storage[uid]);
// storage should be empty.
console.log(storage);
});
link.getFirst().addEvent("mouseenter", function() {
console.log("over");
});
// check to see if it is there via element storage API.
console.log(link.retrieve("events").click);
// check to see if it's there via our puncture
console.log(this.storage[uid]);
// see all events in storage, inc child element:
console.info(this.storage);
what all this proves is, mootools cleans up all you need cleaned. as long as you don't use any inline onclick= stuff on elements you work with, you're going to be fine. Between mootools' garbage collection and the browser, you are well covered. just be aware you can stack up multiple events on a single element if the callbacks are anonymous.
Interesting question... have a read of this: https://developer.mozilla.org/en/DOM/element.addEventListener#Memory_issues
To remove the event listener using jQuery, see http://api.jquery.com/unbind/
I have found that older versions of IE seems to have issues with adding and removing lots of elements with events binded to them. The main cause is circular references that cannot be garbage collected. You can find more information here: http://msdn.microsoft.com/en-us/library/Bb250448
Setting to null will not remove the event, it would just remove the reference to the event. You need to use both element.removeEventListener and element.detachEvent (depending on browser), or if you are using jquery unbind should work.
Also, there are tools available to detect leaks, this one works well (according to coworker): http://blogs.msdn.com/b/gpde/archive/2009/08/03/javascript-memory-leak-detector-v2.aspx