Get source of blur when invoked with jQuery - javascript

I have a form (for user registration) that's paginated via splitting it up into divs that are hidden/displayed with .slideUp()/.slideDown(). It all works pretty well, but I want to validate each page with javascript before they proceed to the next page to reduce the number of errors that come back from the server-side validation. Right now I have them set to validate onblur. To check all the elements when the "next" button is clicked, I want to use .blur() to force it to validate each input, but I'm getting an error. An example of my onblur function is:
var validateEmpty = function() {
var $src = $('.registrationPage #' + event.srcElement.id);
validate($src, "Can't be left empty!", checkEmptyRequired);
}
It's being bound by setting the onblur attribute to the function.
Where validate is a function that takes a jQuery object, an error message, and a function that returns a boolean. The error I get is
TypeError: Cannot read property 'srcElement' of undefined
So it seems clear to me that .blur() doesn't trigger a javascript event and simply calls the onblur function. How can I get the id of a blurred element in a way that will work both when blurred normally (by losing focus in the page) and with .blur()?

You're not showing us how the function is called, but you should probably pass the event as a parameter, otherwise it will be undefined in some browsers (but not in IE):
var validateEmpty = function(event) {
var $src = $('.registrationPage #' + event.target.id);
validate($src, "Can't be left empty!", checkEmptyRequired);
}
that would work with something like:
$('.elements').on('blur', validateEmpty);
Also, srcElement is rarely used (it's IE specific), I think jQuery normalized target, but you could always do something like this to make sure:
var target = event.target || event.srcElement;
It looks like your code is written to work in Internet Explorer only ?

Related

Is there anyway to add items to an existing list in the DOM

My goal is to take user input from a text field and add it directly to a list in the html when the user clicks the "add" button. When I preview the code in my workspace it allows me to enter text in the text field but it will not display in the section I have designated for my list on the web page. The variables at the beginning of the code are for additional processes that I will add later.
My code is:
var buttonone=document.getElementById('add-item-1');
var buttontwo=document.getElementById('add-item-2');
var compareB=document.getElementById('compare');
var resetB=document.getElementById('reset');
//function to add items to first list
function addListOne(addOne,listItem,listOne,list){
addOne = document.getElementById('item-field-1').value;
document.getElementById('list-one-item').innerHTML = addOne;
listOne= document.createElement('li');
listOne.appendChild(document.createTextNode(addOne));
list.appendChild(listOne);
}
buttonone.addEventListener( 'click', addListOne, false);
I have put your code into a jsfiddle at https://jsfiddle.net/s5xzazL1/1/ . Note only the first list and input has the event wired to it.
You can use your browsers developer tools to see the exceptions, e.g. in chrome Menu => More Tools => Developer Tools. If you then see the console you will see the error
Uncaught TypeError: Cannot read property 'appendChild' of undefined
When you enter valuer and click the add button, the list is replace by the text you've added, this is due to your code line
document.getElementById('list-one-item').innerHTML = addOne;
Which is incorrect and should be removed.
The exception how ever is due to the fact that the variable list is null. Yet it is being used as an object in the line
list.appendChild(listOne);
List is null, due to some misunderstanding about the parameters of the click event.
Your code:
function addListOne(addOne,listItem,listOne,list){
...
}
buttonone.addEventListener( 'click', addListOne, false);
Your code is expecting addListOne to be called with 4 parameters, when addListOne is called by the click event only 1 parameter is passed and it is the event.
Therefor you need to find the list yourself with a document.getElementById.
Giving the code
var buttonone=document.getElementById('add-item-1');
var buttontwo=document.getElementById('add-item-2');
var compareB=document.getElementById('compare');
var resetB=document.getElementById('reset');
//function to add items to first list
function addListOne(event){
var newText = document.getElementById('item-field-1').value;
var newListItem= document.createElement('li');
newListItem.appendChild(document.createTextNode(newText));
var listOne = document.getElementById('list-one-item');
listOne.appendChild(newListItem);
}
buttonone.addEventListener( 'click', addListOne, false);
Seen running at https://jsfiddle.net/s5xzazL1/5/

Post comments on Facebook page via console

Like in the image, the Facebook comment box has no submit button, when you write something and press Enter button, the comment posted.
I want to submit the comment via JavaScript that running in console, but I tried to trigger Enter event, submit event of the DOM. Could not make it work.
The current comment boxes aren't a traditional <textarea> inside of a <form>. They're using the contenteditable attribute on a div. In order to submit in this scenario, you'd want to listen to one of the keyboard events (keydown, keypress, keyup) and look for the Enter key which is keycode 13.
Looks like FB is listening to the keydown evt in this case, so when I ran this code I was able to fake submit a comment:
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.querySelector('[role="combobox"]._54-z span span'));
A couple of things to note about this. The class ._54-z was a class they just happened to use on my page. Your mileage may vary. Use dev tools to make sure you grab the right element (it should have the aria role "combobox"). Also, if you're looking to support older browsers, you're going to have to tweak the fireEvent function code above. I only tested the above example in the latest Chrome.
Finally, to complicate matters on your end, Facebook is using React which creates a virtual DOM representation of the current page. If you're manually typing in the characters into the combobox and then run the code above, it'll work as expected. But you will not be able to set the combobox's innermost <span>'s innerHTML to what you're looking to do and then trigger keydown. You'll likely need to trigger the change event on the combobox to ensure your message persists to the Virtual DOM.
That should get you started! Hope that helps!
Some years after, this post remains relevant and is actually the only one I found regarding this, whilst I was toying around trying to post to FB groups through JS code (a task similar to the original question).
At long last I cracked it - tested and works:
setTimeout(() => {
document.querySelector('[placeholder^="Write something"]').click();
setTimeout(() => {
let postText = "I'm a Facebook post from Javascript!";
let dataDiv = document.querySelector('[contenteditable] [data-offset-key]');
let dataKey = dataDiv.attributes["data-offset-key"].value;
//Better to construct the span structure exactly in the form FB does it
let spanHTML = `<span data-offset-key="${dataKey}"><span data-text="true">${postText}</span></span>`;
dataDiv.innerHTML = spanHTML;
let eventType = "input";
//This can probably be optimized, no need to fire events for so many elements
let div = document.querySelectorAll('div[role=presentation]')[1].parentElement.parentElement;
let collection = div.getElementsByTagName("*");
[...collection].forEach(elem => {
let evt = document.createEvent("HTMLEvents");
evt.initEvent(eventType, true, true); //second "true" is for bubbling - might be important
elem.dispatchEvent(evt);
});
//Clicking the post button
setTimeout(()=>{
document.querySelector('.rfloat button[type=submit][value="1"]').click();
},2000);
}, 4000);
}, 7000);
So here's the story, as I've learned from previous comments in this post and from digging into FB's code. FB uses React, thus changes to the DOM would not "catch on" as React uses virtual DOM. If you were to click "Post" after changing the DOM from JS, the text would not be posted. That's why you'd have to fire the events manually as was suggested here.
However - firing the right event for the right element is tricky business and has almost prevented me from succeeding. After some long hours I found that this code works, probably because it targets multiple elements, starting from a parent element of the group post, and drilling down to all child elements and firing the event for each one of them (this is the [...collection].forEach(elem => { bit). As written this can be obviously be optimized to find the one right element that needs to fire the event.
As for which event to fire, as was discussed here, I've experimented with several, and found "input" to be the one. Also, the code started working after I changed the second argument of initEvent to true - i.e. evt.initEvent(eventType, true, true). Not sure if this made a difference but I've had enough hours fiddling with this, if it works, that enough for me. BTW the setTimeouts can be played around with, of course.
(Unsuccessfully) Digging into FB's React Data Structure
Another note about a different path I tried to go and ended up being fruitless: using React Dev Tools Chrome extension, you're able to access the components themselves and all their props and states using $r. Surprisingly, this also works outside of the console, so using something like TamperMonkey to run JS code also works. I actually found where FB keeps the post text in the state. For reference, it's in a component called ComposerStatusAttachmentMentionsInputContainer that's in charge of the editor part of the post, and below is the code to access it.
$r actually provides access to a lot of React stuff, like setState. Theoritically I believed I could use that to set the state of the post text in React (if you know React, you'd agree that setState would be the right way to trigger a change that would stick).
However, after some long hours I found that this is VERY hard to do, since FB uses a framework on top of React called Draft.js, which handles all posts. This framework has it's own methods, classes, data structures and what not, and it's very hard to operate on those from "outside" without the source code.
I also tried manually firing the onchange functions attached to the components, which didn't work because I didn't have the right parameters, which are objects in the likes of editorContent and selectionContent from Draft.Js, which need to be carefully constructed using methods like Modifier from Draft.js that I didn't have access to (how the hell do you externally access a static method from a library entangled in the source code?? I didn't manage to).
Anyway, the code for accessing the state variable where the text is stored, provided you have React dev tools and you've highlighted ComposerStatusAttachmentMentionsInputContainer:
let blockMap = $r["state"].activeEditorState["$1"].currentContent.blockMap;
let innerObj = JSON.parse(JSON.stringify(blockMap)); //this is needed to get the next property as it's not static or something
let id = Object.keys(innerObj)[0]; //get the id from the obj property
console.log(innerObj[id].text); //this is it!
But as I wrote, this is pretty much useless :-)
as I wasn't able to post comments through the "normal" facebook page, I remembered that they also have the mobile version, which is on m.facebook. com, there, they still have the submit Button, so depending on your needs, this may be a good option
so, you could go to the mobile facebook post (eg https://m.facebook.com/${author}/posts/${postId}) and do
// Find the input element that saves the message to be posted
document.querySelector("input[name='comment_text']").value='MESSAGE TO POST';
// find the submit button, enable it and click it
const submitButton = document.querySelector("button[name='submit']");
submitButton.disabled = false;
submitButton.click();
Here is a working solution after 3 weeks of experimenting (using #Benjamin Solum's fireEvent function):
this version posts a comment only for the first post on the page (by using querySelector method)
this version can be used only on your personal wall (unless you change the query selectors)
function fireEvent(type, element, keyCode) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
if (keyCode !== undefined){
evt.keyCode = keyCode;
evt.which = keyCode;
}
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
// clicking the comment link - it reveals the combobox
document.querySelector(".fbTimelineSection .comment_link").click();
setTimeout(function(){
var combobox = document.querySelector(".fbTimelineSection [role='combobox']");
var spanWrapper = document.querySelector(".fbTimelineSection [role='combobox'] span");
// add text to the combobox
spanWrapper.innerHTML = "<span data-text='true'>Thank you!</span>";
var spanElement = document.querySelector(".fbTimelineSection [role='combobox'] span span");
fireEvent("blur", combobox);
fireEvent("focus", combobox);
fireEvent("input", combobox);
fireEvent("keydown", spanElement, 13); // pushing enter
},2000);
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.
to solve your question may you see this link, there is a example how to "Auto comment on a facebook post using JavaScript"
"Below are the steps:
Go to facebook page using m.facebook.com
Sign in and open any post.
Open developer mode in Chrome by pressing Ctrl+Shift+I
Navigate to the console.
Now, run the below script."
var count = 100;
var message = "Hi";
var loop = setInterval(function(){
var input = document.getElementsByName("comment_text")[0];
var submit = document.querySelector('button[type="submit"]');
submit.disabled = false;
input.value = message;
submit.click();
count -= 1;
if(count == 0)
{
clearInterval(loop);
}
}, 10000);
Kind regards
ref.: source page

JQuery preventDefault not working with delegated event

I have a feed that uses AJAX to load in posts once the document is ready. Because the elements aren't ready at code execution, I have to use delegate to bind a lot of functions.
$(posts).delegate('.edit_comment_text','keyup',function(e){
return false;
if (e.keyCode == 13 && e.shiftKey == false) {
//Post the new comment and replace the textbox with a paragraph.
var message_area = $(this).parent('.comment_message');
var new_message = $(this).val();
var comment_id = $(this).closest('.group_comment').attr('data-comment');
var url = 'functions/edit_comment.php';
var array = {
'comment':comment_id,
'message':new_message
}
$.post(url, array, function(data){
console.log(data);
$(message_area).html("");
$(message_area).text(new_message);
});
}
})
This is the code I execute on the event. I've been trying to get the browser to stop dropping down a line when the user hits enter, but this action is performed before my code is even triggered. To prove it, I put the 'return false' at the very top of the block. With that example, none of my code is run when the user hits enter, but the textarea still drops a line.
Is it something to do with JQuery's delegate that causes my function to be called after the default events? They give examples of preventing default events in their documentation, so maybe it's a version bug or something?
Anyone have any ideas?
Thanks!

Onbeforeunload not firing in IE when user closes window

I monitor each field in my form for changes by binding to the 'change' event for all input fields, and set a flag if anything gets modified.
I then bind to window.onbeforeunload and check that flag, returning the warning if the flag is set. Per the Jquery API documentation, I'm binding directly to window.onbeforeunload versus $(window) to avoid memory issues.
For this I have the following code:
$(':input').change(function(){
if($('#editObject_blah').attr('value') == 0){
$('#editObject_blah').attr('value',1)};
}
);
window.onbeforeunload=verifyexit;
function verifyexit() {
if($('#editObject_blah').attr('value') == 1){
return'You have not saved your changes!';
}
};
EDIT: The element editObject_blah is actually this:
<form id="editObject_blah" onSubmit="return false;" value=0>
This works fine in Firefox and Chrome, but fails to catch the user closing the browser window in IE 7.
I should note that the above code is called via an eval() as the result of an ajax request, versus being loaded via an inline script tag. I don't think that should make a difference, but that's why I'm asking here.
Thank you.
You shouldn't use ".attr()" to get the "value" property:
if ($('#editObject_blah').val() == 1) {
return "whatever";
}
If your handler does not return a string, then the browser assumes you're OK with the user closing the window.
If that's not an <input>, then you should probably store the flag with ".data()" instead. To do that, you'd just pick a name for the flag:
$(':input').change(function(){
$('#editObject_blah').data("changed", 1);
});
function verifyexit() {
if($('#editObject_blah').data('changed') == 1){
return "You have not saved changes yet!";
}
}

How to make this jQuery driven page snappier?

I have a contact script. It uses jQuery for it's ajax requests and animations.
I also use it with a hashchange plugin to fix the back button. The slow part lies there.
After finishing the animation of the 'flip', the form fades slowly. The browsers seem to block for a sec. I'm trying to make it snappy (no blocking).
Here is the function responsible for handling the hash change event:
handleHashChange : function () {
// Get the name of the object and cache it
var self = this,
// Get the current hash
hash = window.location.hash,
// Cache the form height var
formHeight = '';
// If the hash is #send or #error, don't do anything
if (hash === "#sent" || hash === "#error") {
return;
}
// Change the page title back to the default
if(self.documentTitle && self.documentTitle != undefined) {
document.title = self.documentTitle;
}
// Reset all inputs in the form
self.inputs.val('').removeAttr('checked').removeAttr('selected');
// Get the height of the form
formHeight = self.getHeight(self.form);
// Show the transition
self.showTransition(self.response, formHeight, function() {
// Show the form
self.form.fadeIn('fast', function() {
// Show the button
self.button[0].style.display = 'block';
// Focus the first input
self.inputs[0].focus();
})
})
}
The whole code can be seen from the link below, it's fully documented:
http://www.coolcontact.co.cc/beta/1.3/js/main.js
You can see I have used a lot of tips I found on the internet to optimize this script, except using javascript's native 'for' in place of '$.each()' , but it's not that big of a deal here.
If anyone wants to see the slowness, try sending an empty message (validation is disabled) from the link below then click the back button in your browser:
(note: it's not in English, but guess it's pretty self-explanatory ^^)
http://www.coolcontact.co.cc/beta/1.3/
So how can I make it more snappy?
I think it's pretty quick already, but here's something I noticed with your code.
This "if" statement is a bit redundent.
if(self.documentTitle && self.documentTitle != undefined) {
document.title = self.documentTitle;
}
The call to "self.documentTitle" will return evaluate to "false" if its value is "undefined", so you don't need the second "self.documentTitle != undefined".
You could just use the follwing instead:
if(self.documentTitle){
document.title = self.documentTitle;
}
Remember, the values false, null, undefined, 0 and an empty string all evaluate to a false boolean value.

Categories

Resources