insertAfter() is duplicating an element when I don't want it to - javascript

Okay, so I'm making a chatbot, and I ran into a problem. So I need to make a function that creates a chat message everytime the enter key is pressed. So far, it's coming out nicely, just one problem. It's duplicating an element, that I only want one of.
To see what I'm talking about, go to http://jsfiddle.net/MatthewKosloski/BHXMa/ and type out a message, then hit enter. Notice how there are two "Foo!" messages? I only want one. I'd also want to make these messages go from top-bottom in chronological order, but I can't do that until I find out why this duplication is occuring!
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
var robotMessage = userMessage;
function intelResponse(){
// ROBOT
var robot = document.createElement("h4");
var robotText = document.createTextNode("Robot");
robot.appendChild(robotText);
robot.className = "rtitle";
document.body.appendChild(robot);
insertAfter(userMessage, robot);
// Robot's response
robotMessage = document.createElement("span");
var robotMessageText = document.createTextNode("FOO");
robotMessage.appendChild(robotMessageText);
robotMessage.className = "rmsg";
document.body.appendChild(robotMessage);
insertAfter(robot, robotMessage);
}

A very simple mistake . You are calling the method intelResponse twice. Fixed the issueFIDDLE
if (e.keyCode == 13) {
submitUserMessage();
//intelResponse();
$("#user-input").val("");
}

You're calling "intelResponse" from the "submit" routine, and also explicitly after you call the "submit" routine.

The problem lies at the end of submitUserResponse function, where you also calling intelResponse

The message is appending twice because you call the function twice.
One time on keyup:
$(document).keyup(function (e) {
if (e.keyCode == 13) {
submitUserMessage();
intelResponse();
$("#user-input").val("");
}
});
And in the submitUserMessage.
Comment one of those and it doesnt duplicate: http://jsfiddle.net/BHXMa/2/

Related

Tracking users last form field on page unload

I am trying to store the last page and the last form field that the user was focused on prior to his unexpected exit from the page (did not click continue), but my solution is not working.
I am using the onbeforeunload event on the pages themselves, I fully realise that this event does not work consistently across all the browsers, but I could not figure out a way to this in an another way.
window.onbeforeunload = function () {
if (fieldName != null && fieldName.length > 0) {
var formName = location.pathname.substring(1);
if (typeof (TrackFormField) == 'function') {
try {
TrackFormField(formName, fieldName);
}
catch (err) {
}
}
}
};
TrackFormField is function in a separate file that just assigns the value to the property
function TrackFormField(formName, fieldName) {
if (formName) {
s.prop23 = formName + ":" + fieldName;
}
sendOmniture();
}
And sendOmniture does the following:
function sendOmniture() {
var s_code = s.t(); if (s_code) document.write(s_code)
}
The weird thing is that at times it works, but usually I don't see the prop23 neither in the analytics debugger nor in Fiddler.
After some debugging I found out that s_code for some reason is undefined in the send omniture function.
What can I do to fix this issue ?
My guess is the window.onbeforeunload event will kill the (s) object in most cases before it has a chance to execute.
What about using a Direct Call rule with Adobe DTM that will fire your function on focus change? That way you don't need to tie any action to the onbeforeunload event.
Hope this helps.

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!

Catching A Browser Close Event

Hello Seniors (As I am new to Web Based Applications),
I was keen to implement or catching browser closing event.
Yes! I did it and successfully implemented it by using javascript{see code below}
but I have implemented it in a web page without MasterPage.
Now, as I am trying to implement it in a webpage with MASTERPAGE but in each post back...the event window.onunload is caught, which is giving me problems...
Is there any technique or logic to detect whether I can differentiate between a Close browser button and a page's post back event.
Please guide me...as I have to implement in a project as soon as possible....
thank you.
Ankit Srivastava
<script type="text/javascript">
function callAjax(webUrl, queryString)
{
var xmlHttpObject = null;
try
{
// Firefox, Opera 8.0+, Safari...
xmlHttpObject = new XMLHttpRequest();
}
catch(ex)
{
// Internet Explorer...
try
{
xmlHttpObject = new ActiveXObject('Msxml2.XMLHTTP');
}
catch(ex)
{
xmlHttpObject = new ActiveXObject('Microsoft.XMLHTTP');
}
}
if ( xmlHttpObject == null )
{
window.alert('AJAX is not available in this browser');
return;
}
xmlHttpObject.open("GET", webUrl + queryString, false);
xmlHttpObject.send();
return xmlText;
}
</script>
<script type="text/javascript">
var g_isPostBack = false;
window.onbeforeunload = check ()
function check()
{
if ( g_isPostBack == true )
return;
var closeMessage =
'You are exiting this page.\n' +
'If you have made changes without saving, your changes will be lost.\n' +
'Are you sure that you want to exit?';
if ( window.event )
{
// IE only...
window.event.returnValue = closeMessage;
}
else
{
// Other browsers...
return closeMessage;
}
g_isPostBack = false;
}
window.onunload = function ()
{
if ( g_isPostBack == true )
return;
var webUrl = 'LogOff.aspx';
var queryString = '?LogoffDatabase=Y&UserID=' + '<%# Session["loginId"] %>';
var returnCode = callAjax(webUrl, queryString);
}
</script>
There is no javascript event which differentiates between a browser being closed and the user navigating to another page (either via the back/forward button, or clicking a link, or any other navigation method). You can only tell when the current page is being unloaded. Having said that, I'm not sure why you'd even need to know the difference? Sounds like an XY problem to me.
The answer can be found on SO:
How to capture the browser window close event?
jQuery(window).bind("beforeunload", function(){return confirm("Do you really want to close?") })
and to prevent from confirming on submits:
jQuery('form').submit(function() {
jQuery(window).unbind("beforeunload");
...
});
First step: add global JavaScript variable called "_buttonClicked" which is initially set to false.
Second step: have every button click assign _buttonClicked value to true.. with jQuery it's one line, pure JavaScript is also few lines only.
Third step: in your function check _buttonClicked and if it's true, don't do anything.
EDIT: After quick look in your code I see you already have steps #1 and #3, so all you need is the second step, assign g_isPostBack as true when any submit button is clicked. Let me know if you need help implementing the code and if you can have jQuery.
If one wants to catch Log out when the browser is closed (by clicking on the cross), we can take the help of window events.
Two events will be helpful: onunload and onbeforeunload.
But the problem arises that the code will also work if you are navigating from one page to another as well as also when one
refreshes the page. We don't want our sessions to be clear and inserting the record of logging out while refreshing.
So the solution is if we distinguish the difference between closing and refreshing or navigating.
I got the solution:
Write 'onbeforeunload ="loadOut();"' within the body tag on master page.
Add the following function inside script in head section of master page :-
function loadOut() {
if ((window.event.clientX < 0) || (window.event.clientY < 0))
{
// calling the code behind method for inserting the log out into database
}
}
And its done. It is working for IE, please check for other browsers. Similarly you can detect the event if the window is closed
by pressing the combination of keys ALT+F4.
window.unload fires when we navigate from one page to another as well as when we click on close button of our browser,So to detect only browser close button you need to use flag.
var inFormOrLink;
$("a,:button,:submit").click(function () { inFormOrLink = true; });
$(":text").keydown(function (e) {
if (e.keyCode == 13) {
inFormOrLink = true;
}
})/// Sometime we submit form on pressing enter
$(window).bind("unload", function () {
if (!inFormOrLink) {
$.ajax({
type: 'POST',
async: false,
url: '/Account/Update/'
});
}
})

Code works when i put javascript alert in between anywhere

I have a function that runs on key tab press, it works fine when i put a javascipt alert in between the code, any kind of alert,if i remove the alert it stops working : my function
//Function to set the tab feture for focus to work properly on fields with autosuggestion(location and name)
function setFocusOnTab(name) {
var focusElement = "";
if(name == "name") {//For main contact field
if ($("#email").is(":visible")) {
$('#email').focus();
}
} else if(name == 'location_name') {//For main contact field
$("#country").focus();
} else {//For extra contact field
var outputDataCurrentVal = name.lastIndexOf('record_');
if(outputDataCurrentVal < 0) {
//ADDTIONAL CONTACT TAB
var outputDataCurrentName = name.lastIndexOf('_name_');
if(outputDataCurrentName >= 0) {
//Replacing the name to get location name.
locName = currentName.replace("_name_","_designation_");
focusElement = locName;
} else {
var outputDataCurrentLoc = name.lastIndexOf('_location_');
if(outputDataCurrentLoc >= 0) {
//Replacing the location name to get country name.
countryName = name.replace("_location_","_country_");
focusElement = countryName;
}
}
} else {
//Extra CONTACT TAB
var outputDataCurrentName = name.lastIndexOf('_name_');
if(outputDataCurrentName >= 0) {
//Replacing the name to get location name.
locName = currentName.replace("_name_","_location_");
focusElement = locName;
} else {
var outputDataCurrentLoc = name.lastIndexOf('_location_');
if(outputDataCurrentLoc >= 0) {
//Replacing the location name to get country name.
countryName = name.replace("_location_","_country_");
focusElement = countryName;
}
}
}
$("#" + focusElement).focus();
return false;
}
}
Sounds like you need something to halt your code, which is what alert() does.
You may need a callback instead.
What might be happening with alert() is that calling it causes the current window to lose focus (as focus moves to the new pop-up dialogue box), and after it's finished it re-focuses the window. This will trigger focus and blur events which might confuse your script, and in Safari the window may not re-focus at all.
It's not clear to me what you are doing here... how are you attaching this code to a tab key event? What event is supposed to be cancelled by the return false;? If you are using keypress, then that simply won't get called for the Tab key in IE, Safari or Chrome. If you are using keydown, then cancelling the event won't prevent the tabbing in Opera. And what about Shift-Tab?
Reproducing/altering browser keyboard behaviour is hard: it's much better not to if there's any other way. For making controls like a drop-down suggester work you are probably much better off just setting the declarative tabIndex on the elements concerned and letting the browser work out how to sort out the tabs from there.
I should write something on this post. I read so many blogs and post but couldn't get the right solution from anywhere else. Even in this post, I looked more detail and tried each of solution.
Finally reading the answer of bobince, I could figured out the solution. In my case, I have set the focus to another textbox ( not required type) and later when I finish my job, I set back focus to original one. So the morel story is we just need to set focus somewhere else from current element which actually done by alert and I have replaced that on by setting the focus on non required element.

Categories

Resources