I am writing a small code with JavaScript (using jQuery and Knockout) and HTML that takes user input (GitHub username), checks if the input is valid against a GitHub api, and displays the user's GitHub avatar and username (linked to the matching profile on GitHub). The display replaces the form in which the user entered the username.The original HTML before the user inputs is:
<div id="inputSection">
<form>
<p>
GitHub Username:
<input type="text" name="username" value="" placeholder="username" id="un"/>
<button type="button" id="submitButton">Login</button>
</p>
</form>
</div>
And the code to replace it is this:
$("#submitButton").click(function() {
var username = document.getElementById('un').value;
var inputForm = $(document.getElementById('inputSection'));
$.ajax( {
...
success: function () {
alert("Welcome, " + username);
var userURL = 'https://github.com/' + username;
var inputContent = $('<a data-bind="attr: {href: userURL}"><img data-bind="attr: {src: avatar_url}" height=\"30\" width=\"30\"/>' + username + '</a>');
$(inputForm.replaceWith(inputContent));
}
});
});
It seems to work for the most part. After the alert welcomes the user by username, the form disappears from the webpage. It is replaced by the username, which is formatted like a link. However, it does not function as one. Clicking it does not do anything. Also, the user's avatar, despite showing a box of the set size on the webpage, does not appear.The solution is likely very simple and obvious, but as I only started learning these languages and libraries this week, I am not sure what is going wrong. Knockout should be running on the HTML page that calls the JavaScript page, and the ajax is working with regards to other functions, so I assume that's fine. The value "avatar_url" is a part of the api requested with ajax at https://api.github.com/users.I've tried all sorts of different things to no effect. If you want any more information or have a suggestion to make this question better, please comment. I'm new to coding and Stack Overflow, but I want to make both my programs and my questions as good as possible. Thank you for your time.
EDIT: 1. I originally failed to set a size for the image, resulting in a 0x0 image. This has been corrected, though the image itself still does not display. 2. When I first put in my code, I tried to make it easier to read by excluding where some variables had been renamed for other, unrelated portions and just making all the names match between the two relevant snippets. I did not catch them all. They should all match now.
Short answer:
You’re inserting an html element with data-binds without explicitly initializing its bindings. Use ko.applyBindings(vm, node) on the newly injected part of the DOM.
Long answer:
If you're new to coding and to both jQuery and knockout, I'd suggest not using both libraries at once. Here’s why:
If you want to use knockout, you'll have to stick to a certain kind of software architecture:
Simplify dynamic JavaScript UIs with the Model-View-View Model (MVVM) (http://knockoutjs.com/)
jQuery, on the other hand, is more of a toolbox. It doesn't dictate an architectural pattern.
It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. (https://jquery.com/)
This might sound a bit lame, and isn't really an answer, but I'll show you the differences between solving your problem the “knockout way”, and “the jQuery way”. I’ll start with the latter, since it’s closest to your current approach:
The jQuery approach (note that I'm skipping the Ajax part)
Find the elements you need to make your UI interactive. Attach event listeners to buttons, modify the DOM when new data is available.
$(document).ready(function() {
// Notice that you don't need document.getElementById
var submitButton = $("#loginButton");
var userNameInput = $("#un");
var inputSection = $("#inputSection");
var getContentString = function(userName) {
var userUrl = "https://github.com/" + userName;
var avatarUrl = "...";
// Inject the user specific attributes
return "<a href=`" + userUrl + "`><img src=`" + avatarUrl + "` height='30' width='30'/>" + userName + "</a>";
};
var onSubmitClick = function(event) {
var userName = userNameInput.val();
var onSuccess = function() {
// Create new <a> element and replace the form with the new HTML
var inputContent = $(getContentString(userName));
inputSection.replaceWith(inputContent);
};
/*
$.ajax({
success: onSuccess
});
*/
//Just call onSuccess to circumvent unimplemented ajax:
onSuccess();
};
submitButton.click(onSubmitClick);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="inputSection">
<p>
GitHub Username:
<input type="text" name="username" value="" placeholder="username" id="un" />
<button type="button" id="loginButton">Login</button>
</p>
</form>
The knockout approach
Create a viewmodel for your user. Bind the input and compute the other properties automatically. Attach event listeners through data-binds. Use if, visible or template bindings to swap out parts of the UI.
var UserViewModel = function() {
this.userName = ko.observable("");
this.confirmed = ko.observable(false);
this.userUrl = ko.computed(function() {
return "https://github.com/" + this.userName();
}, this);
this.avatarUrl = ko.computed(function() {
return "???" + this.userName();
}, this);
};
UserViewModel.prototype.confirm = function() {
/* ajax (disabled for example)
$.ajax({
success: this.confirmed.bind(null, true)
});
*/
this.confirmed(true);
};
var viewModel = {
user: new UserViewModel()
};
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="with: user">
<!-- ko ifnot: confirmed -->
<form>
<p>
GitHub Username:
<input data-bind="value: userName" type="text" placeholder="username" />
<button data-bind="click: confirm">Login</button>
</p>
</form>
<!-- /ko -->
<!-- ko if: confirmed -->
<a data-bind="attr: { href: userUrl }">
<img data-bind="attr: {src: avatarUrl }" />
<span data-bind="text: userName"></span>
</a>
<!-- /ko -->
</div>
With help from user3297291's jQuery answer above, I eventually came to this. The answer was good and necessary for this progression; some parts just did not work for this situation (mostly simple compatibility issues with other code not included in this sample). Though this is a very specific question, I thought the solution should be included. Note that I have decided to stay away from Knockout for now.
The HTML suggestion to attach the id to the form rather than the div is a good move.
$("#submitButton").click(function inputForm() {
var username = $("#un").val();
function makeUserContent(user, profile, avatar) { //arguments are data from ajax
//writes a string without the messy quotes within quotes within quotes problem
//much clearer than trying to handle the jQuery and string all at once
return "<img src=" + avatar + " height='30' width='30' />" + user + "";
}
function submitUsername() {
$.ajax({
...
success: function correntInformation(data) {
//data is what the ajax gets, which is passed for use
alert("Welcome, " + username + ".");
//calls to make the string with the data gotten by ajax
var inputContent = $(makeUserContent(data.login, data.html_url, data.avatar_url));
$("#inputSection").replaceWith(inputContent);
}
})
}
submitUsername();
})
The biggest things I took away from this problem is this: simplify strings, hold and use data, work with one library at a time (until experienced with both).
Related
I have a form where I have submitted the text from the user input but I dont know how to show the text to a different div in a another page in javascript. can anyone help me solve this issue? thanks for the help.
here is my script:
<input onclick="addTheEvent(); return false;" type="submit" value="Add to list" class="btn btn-primary" />
<script>
var addToTheContent = document.getElementById("canvas");
var scheduleEvent = document.getElementById("scheduleStartTime");
var candidateId = document.getElementById('candId');
var getCandId = document.getElementById("candId");
var displayCandId = getCandId.options[getCandId.selectedIndex].value;
function addTheEvent() {
addToTheContent.innerHTML = "name = " +
displayCandId + " at " + scheduleEvent.value;
}
</script>
Another page: (I want to add the value to show in my content div that is in another page)
<pre id="content" style="white-space: pre-wrap;"></pre>
You can use a single page application (SPA).
A single-page application is an app that works inside a browser and does not require page reloading during use.
But the SPA is quite a massive theme. It's required for knowledge of the usage browser history, ajax , etc. So you should try to use variant #2
You can use local localStorage of the browser where you can save your data
localStorage.setItem("key","value") //set value into local storage
localStorage.getItem("key") // get value
The "key" is an any string
On another page use
document.addEventListener("DOMContentLoaded", function(event) {
const dataFromLocalStorage = localStorage.getItem('key');
document.querySelector("#content").innerHTML = dataFromLocalStorage; })
I'm learning JS and I need some help figuring out why my info isn't getting populated in the html. I'm just trying to get the basic functionality to work, so that I can continue to expand on it.
User is supposed to input a 3 digit route value, which will then return all the route information from an api call. I was able to get the route info to display earlier when I got the api call set up, but I'm struggling to figure why it's not displaying now that I tried adding in a feature to allow the user to input the route. See attached pen
HTML
<div class='container'>
<h1 id='header'>Route Info</h1>
<input id="input" type="text" placeholder="Enter 3 digit route ex 005" >
<input type="button" value="Get Route" onclick="getRoute()">
<br>
<p id = 'p'><span id="routeInfo"></span></p>
</div>
Javascript
$(document).ready(function() {
var route = $('#input');
getRoute.click(function() {
var scriptTag = document.createElement('SCRIPT');
scriptTag.src = "https://wsdot.wa.gov/Traffic/api/Bridges/ClearanceREST.svc/GetClearancesAsJson?AccessCode=59a077ad-7ee3-49f8-9966-95a788d7052f&callback=myCallback&Route=" + route;
document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
var myCallback = function(data) {
var myarray = Array.prototype.slice.call(data);
document.getElementById("routeInfo").innerHTML = JSON.stringify(myarray);
}
});
});
It looks like you are jumping through a lot of hoops you don't need to. As long as you are using Jquery, you should look into getting the api data with an ajax request. It's much easier and more intuitive. Also you have a few problems such as trying to get the input value with var route = $('#input'); which return the actual input element. You are also processing the returned data in a way that won't work.
Here's a basic example to get you going on (IMO) a better track:
function getRoute() {
var route = $('#input').val();
var url = "https://wsdot.wa.gov/Traffic/api/Bridges/ClearanceREST.svc/GetClearancesAsJson?AccessCode=59a077ad-7ee3-49f8-9966-95a788d7052f&Route=" + route;
$.ajax({url: url, success: function(data){
var retValue = "";
var i = 0
for(i; i< data.length; i++) {
retValue += data[i].BridgeName + "<br>"
}
document.getElementById("routeInfo").innerHTML = retValue;
}});
}
If you intend functionality in the getRoute.click callback to run, you need to rewrite that as a method function getRoute(), or get the button element via jQuery and assign that to the variable getRoute. As it stands, you have the click method wired via the markup to a function named getRoute which does not exist. In the JS you are trying to register a click event to a jQuery object named getRoute which does not exist.
getRoute needs to be a global function for it to be called from html :
getRoute = (function() {
Also, myCallback needs to be a global function for it to be called from your loaded script (just remove the var):
myCallback = function(data) {
So I have a simple search form at the top of my document which is ment for tags. It looks like this:
<div id="search">
<form id="srch">
<input type="text" placeholder="No filthy keywords pls." id="sbx"/>
<i class="fa fa-search"></i>
</form>
</div>
And I want the user to write a tag into the "search" field and when he presses the submit button it gets the photos based on the tag he supplied and fetches photos from flickr and fills a #container div with them. The jQuery I have so far looks like this:
var searchTerm = $("#sbx").val();
var Flickurl = "https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=376b144109ffe90065a254606c9aae3d&";
var tags = "&tags=" + searchTerm;
var tagmode = "&tagmode=any";
var jsonFormat = "&format=json";
var FinalURL = Flickurl + tags + tagmode + jsonFormat;
$(document).ready(function() {
$("#btn").click(function(event){
$.getJSON('FinalURL', function(photo) {
$('#content').append('<img src="' + 'https://www.flickr.com/photos/' + photo.owner + '/' + photo.id + '"/>');
});
});
});
I cannot find any help anywhere, I'm doing this as a schools project and I have never, ever done anything with API's I think I'm misunderstanding something in the jquery and api documentation because this is not doing anything at all lol..
I will be super happy if there is anyone who could help me with this, I feel like the code should be good but maybe I'm missing something small.. ?
You are fetching the string 'FinalURL', which will be resolved to CURRENT_URL/FinalURL, try this
$.getJSON(FinalURL, ... (without the quotes ')
Ok i found some other errors too.
Change your var jsonFormat to this "&format=json&nojsoncallback=1" to get jsonrawdata from the api.
The json returned isn't one photo, it's a list of all photos, so to access one photo you have to do this: response.photos.photo[NUMBER]
Your url is pointing to a flickr page and not to an image source, it should be like this: https://farm{farm-id}.staticflickr.com/{server-id}/{id}_{secret}.jpg
I fixed your errors in this fiddle: JSFiddle
Something like this will work. You just needed to change how you were populating the url queries into a javascript object.
http://jsfiddle.net/669jy9am/
Obviously this won't work on JSFiddle, but if you put that into your own page, it should work.
<div id="search">
<form class="search--form">
<input type="text" placeholder="No filthy keywords pls." id="sbx" />
<button type="submit" id="btn">Search<i class="fa fa-search"></i>
</button>
</form>
function searchFlickr() {
var searchTerm = $("#sbx").val();
var Flickurl = "https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=376b144109ffe90065a254606c9aae3d&";
$("form").submit(function (event) {
event.preventDefault();
var searchTerm = $('#sbx').val();
$.ajax({
url: 'Flickrurl',
data: {
format: "json",
jsoncallback: 1,
tags: searchTerm
},
}).done(function (data) {
//Populate the images with the data
});
});
}
$(document).ready(function () {
searchFlickr();
});
Using widget script--http://socialmention.com/tools/-- from Social Mention. Tried to modify by adding input box to allow user to change social media topic (var smSearchPhrase). I created a function [smSearch()] to retrieve user data (smTopic), assign it to a new variable (var newTopic) and then assign that value to var smSearchPhrase. The assignment does not work.
The function appears to work based on values observed via alerts, however, I cannot figure out how to assign the value from var newTopic to var smSearchPhrase inside the script. I experimented by placing script inside the function, but that didn't work either. Any assistance is appreciated.
If I failed to include all necessary information, please advise. Thanks for any assistance.
HTML:
<form>
<label for="smTopic">Enter topic:</label>
<input type="text" id="smTopic">
<button onclick="smSearch()">Submit</button>
<input type="reset">
</form>
Function: (includes alerts to check values)
function smSearch(){
var newTopic=document.getElementById("smTopic").value;
if(newTopic === ""){
alert("Please enter new social media topic.");
}else{
alert("New topic: " + newTopic);
smSearchPhrase = newTopic;
alert("Value of smSearchPhrase: " + smSearchPhrase);
}
Script: var smSearchPhrase in original has value assigned, e.g. var smSearchPhrase = 'social mention';
<script type="text/javascript">
// search phrase (replace this)
var smSearchPhrase;
// title (optional)
var smTitle = 'Realtime Buzz';
// items per page
var smItemsPerPage = 7;
// show or hide user profile images
var smShowUserImages = true;
// widget font size in pixels
var smFontSize = 11;
// height of the widget
var smWidgetHeight = 800;
// sources (optional, comment out for "all")
//var smSources = ['twitter', 'facebook', 'googleblog', 'identica'];
</script>
<script type="text/javascript" language="javascript" src="http://socialmention.s3.amazonaws.com/buzz_widget/buzz.js"></script>
I think your form is submitting back to the backend, you need to stop the form from doing that by returning false from onsubmit or canceling the event.
So this should work:
<form onsubmit="return smSearch();">
<label for="smTopic">Enter topic:</label>
<input type="text" id="smTopic">
<button>Submit</button>
<input type="reset">
</form>
And return false in your JavaScript:
function smSearch(){
var newTopic=document.getElementById("smTopic").value;
if(newTopic === ""){
alert("Please enter new social media topic.");
}else{
alert("New topic: " + newTopic);
smSearchPhrase = newTopic;
alert("Value of smSearchPhrase: " + smSearchPhrase);
}
return false;
}
Personally I'd use preventDefault() on the event argument (not shown here), but that only works across all browsers when you also include a JavaScript library like jQuery as some versions of IE use a bubble property on the event or something.
I have some ajax on my web page, that is triggered via a click event, the javascript in question looks like this,
$('.career_select .selectitems').click(function(){
var selectedCareer = $(this).attr('id');
$.ajax({
type: 'POST',
url: '/roadmap/step_two',
data: 'career_choice='+selectedCareer+"&ajax=true&submit_career=Next",
success: function(html){
$('.hfeed').append(html);
buildSelects();
$('.grade_options .selectitems').addClass('select_1')
}
});
});
This part of the ajax request works fine. What happens on success is that I load in another view into my page, this view has some more user interaction that fires some more ajax however, it just fires the previously used method, where as it should be doing the following,
$('.grade_options .selectitems').click(function(){
var selectedGrade = $(this).attr('id');
alert(selectedGrade);
})
The HTML+PHP looks like this,
<div class="grade_options">
<input value="" name="grade" class="customselect" type="hidden">
<div class="iconselect">Have you got any of the following?</div>
<div style="display: none;" class="iconselectholder">
<div class="selectoptions">
<div id="1" class="selectitems hoverclass selectedclass select_1">
<span>Accountant</span>
</div>
<div id="2" class="selectitems">
<span> Grade D's at GCSE including English and Maths</span>
</div>
<div id="3" class="selectitems">
<span>3 GCSE's at grade B and 3 GCSEs at grade C or equivalent and you must have achieved at least a grade C in GCSE English Language & B in Maths</span>
</div>
</div>
</div>
<noscript>
<input type="submit" value="Next" name="submit_grades" class="arr" />
</noscript>
</div>
The .selectitems get created from a select menu using this plugin,
$.fn.customSelect = function() {
// define defaults and override with options, if available
// by extending the default settings, we don't modify the argument
return this.each(function() {
obj = $(this);
obj.after("<div class=\"selectoptions\"> </div>");
obj.find('option').each(function(i){
$(".selectoptions").append("<div id=\"" + $(this).attr("value") + "\" class=\"selectitems\"><span>" + $(this).html() + "</span></div>");
});
obj.before("<input type=\"hidden\" value =\"\" name=\"" + this.name + "\" class=\"customselect\"/><div class=\"iconselect\">" + this.title + "</div><div class=\"iconselectholder\"> </div>")
.remove();
$('.iconselectholder').hide();
$(".iconselect").click(function(){
$(".iconselectholder").toggle("slow");});
$(".iconselectholder").append( $(".selectoptions")[0] );
$(".selectitems").mouseover(function(){
$(this).addClass("hoverclass");
});
$(".selectitems").mouseout(function(){
$(this).removeClass("hoverclass");
});
$(".selectitems").click(function(){
$(".selectedclass").removeClass("selectedclass");
$(this).addClass("selectedclass");
var thisselection = $(this).html();
$(".customselect").val(this.id);
$(".iconselect").html(thisselection);
$(".iconselectholder").toggle("slow")
});
});
// do the rest of the plugin, using url and settings
}
I am struggling to see any reason as to why my second ajax request is running the method of the first ajax request.
Your code seems somewhat incomplete, but I think I can help you.
Where is the class .career_select in the HTML+PHP example you have given? My guess is that .career_select is wrapping .grade_options due to your append:
$('.hfeed').append(html) am I correct? .grade_options was part of the html that got appended right?
If I am correct, then the newly appended HTML would not have had event handlers tied to it ahead of time and hence your second event handler is not firing. I think there are two things you can do:
Declare the new event handler for $('.grade_options .selectitems') in the success function of the first event handler AFTER the append.
If that doesn't work then just do what Paul Sweatte instructed you to do (look at the comments), unbind the original click event in the success callback or if you are sure it is a one-off thing, take a look at jQuery's $(selector).one().
I hope this helps. If the second one works, please remember to give points to Paul Sweatte's comment.