Chrome is outputting something different than other browsers/IDE - javascript

Problem
I am making a wikipedia viewer for FreeCodeCamp. When I execute my code using Jetbrains Webstorm IDE, I get a total of 10 extra divs created at the end of my search results.
When I do so using JSfiddle, I see the correct results, which should just be the initial 10 search results, and not +10 more empty div containers.
Any idea what's going on?
Troubleshooting
After some more digging into it, it seems like this is more of a browser issue than an IDE issue. It looks like it's just Chrome that's doing this? I tried on Firefox and IE, and it runs as normal, just like the JSfiddle. I have also tried clearing all cookies and cache on Chrome.
When I open the console log and look at the elements, in Chrome, there are clearly 10 extra empty divs, as shown here.
But in Firefox, the same code does NOT create the divs, as shown here.
Edit: Okay, so in Chrome, if you hit enter, instead of clicking on the search button, it creates the extra divs. But if you click on the search icon, the results appear correctly. Can anyone explain why? Did I place/write the .keyup() code block incorrectly?
Here's a picture of said issue.
Javascript portion:
$(function(){
$("#search-bar").keyup(function(event) {
if(event.keyCode === 13) {
$(".btn").click();
}
});
$(".btn").click(function() {
$("#results").empty();
var searchTerm = $("#search-bar").val();
var url = "https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=" + searchTerm + "&utf8=&format=json&origin=*";
console.log(url);
$.ajax({
cache: false,
url: url,
type: 'GET',
success: function(data) {
// Create a blank array to store the specific URLs in
var urlArr = [];
for (var i=0; i <= data.query.search.length; i++) {
// Adding the variables for use here
var headerData = data.query.search[i].title;
var urlSearchTerm = headerData.replace(/\s/gi, '_');
var snippetData = data.query.search[i].snippet + "...";
var createDiv = document.createElement("div");
var createHeader = document.createElement("h1");
var createSnippet = document.createElement("p");
var divId = "div" + i.toString();
var headerId = "header" + i.toString();
var snippetId = "snippet" + i.toString();
var resultUrl = "https://en.wikipedia.org/wiki/";
urlArr[i] = resultUrl + urlSearchTerm;
// Create the div element, give it an id
// Create <h1> element, give it an id
// Create <p> element, give it an id
// Give each <div> a class
createDiv.setAttribute("id", divId);
createHeader.setAttribute("id", headerId);
createSnippet.setAttribute("id", snippetId);
createDiv.setAttribute("class", "each-result");
// appending <div> elements in the #results id element in body
// appending <h1> element to the div element
// appending <p> element to the div element
document.getElementById("results").appendChild(createDiv);
document.getElementById(divId).appendChild(createHeader);
document.getElementById(divId).appendChild(createSnippet);
// populate json data into <h1> element
// populate json data into <p> element
$("#header" + i.toString()).text(headerData);
$("#snippet" + i.toString()).html(snippetData);
// create a click event handler that does 2 things
// 1. Gives each div an element that has a URL in the resultUrl array
// 2. Opens that url in a new window
(function(i) {
$("#div" + i.toString()).click(function() {
window.open(urlArr[i]);
});
}(i));
}
}
});
});
$("#random-wiki-button").click(function() {
window.open("https://en.wikipedia.org/wiki/Special:Random");
});
});
Actual full code including html/css + JS: JSfiddle

Maybe try updating chrome?
I would try to change your for loop to for (var i=0; i < 10; i++) { that way the increment stops at the 10th item.
edited.

Related

How do I set up an on.click for multiple generated elements?

As a student project, we are trying to build a website that gives recommendations for movies. After recommendations are generated we want the user to be able to click on any of the movie posters in order to pull up more information about that specific movie. The .on('click') currently selects all of the results which are not ideal...
As it stands this is what we have:
axios.get(omdbCall + movieTitles)
.then(function(response){
let movies = response.data.results;
for (i=0; i < movies.length; i++) {
var posterPath = movies[i].poster_path;
var movieID = movies[i].id;
var movTitle = movies[i].title;
var movImg = "https://image.tmdb.org/t/p/w92";
$('#movPoster').append('<img class="posters" src=' + movImg + posterPath + '>');
}
$(".posters").on("click", function () {
console.log("I clicked a poster!");
})
})
We also tried changing the rendered img tag to include an id based on the movie title or its imdbID. We tried using this selector for both attempts:
$("#" + movTitle)
With this change in the append function:
$('#movPoster').append('<img id=' + movTitle + ' src=' + movImg + posterPath + '>');
I expected to be able to select just one element but that ain't what's happening. I hope I explained properly and in enough detail. Any help would be greatly greatly appreciated. Thank you!
you are making .on('click') event direclty on dynamically generated html. This won't work. Because when the script was loaded initially, there is no element with class posters.
You have to use something like this
$("#not_dynamic_element").on("click", ".posters", function(){
// Code here
});
The logic is you have to select an element that is not dynamically loaded. i.e, a static element which is an ancestor of posters class.
For example say you have a div with class posters-container which is already present on page load. You are appending the img tag with class posters to this div. So you need to get click on all img tag with class posters, you could write,
$(".posters-container").on("click", ".posters", function(){
// Code here
});
Hope you understood the logic and what the issue was.
UPDATE - LOGIC ISSUE IN FIDDLE
I see what's wrong in your fiddle. I am trying to make it simple. So check this code you have written
axios.get(finalSearch)
.then(function(response){
// console.log(response);
let movies = response.data.Similar.Results;
// let posters = response.data.results.poster_path;
for (i=0; i < movies.length; i++){
// console.log(movies[i].Name);
var movArr = movies[i].Name;
var movStr = movArr.split(" ");
var movieTitles = movStr.join("+")
getMoviePosters(movieTitles);
}
})
.catch(function(err) {
console.log(err);
})
In this code you can see that you are calling the function getMoviePosters(movieTitles) inside a for loop. Your for loop contains the following line which you use to select the dynamically generated movie poster element.
$("#movPoster").on("click", function () {
console.log("I clicked a poster!");
})
So i would suggest you to call this click function after the for loop as shown below (Remove the previous code). Also add posters class to click function.
axios.get(finalSearch).then(function(response){
// console.log(response);
let movies = response.data.Similar.Results;
// let posters = response.data.results.poster_path;
for (i=0; i < movies.length; i++){
// console.log(movies[i].Name);
var movArr = movies[i].Name;
var movStr = movArr.split(" ");
var movieTitles = movStr.join("+")
getMoviePosters(movieTitles);
}
$("#movPoster").on("click", '.posters', function () {
console.log("I clicked a poster!");
})
})
reason
Maybe when the code$(".posters").on("click",...) runs while img.posters or #movPoster still not rendered in html.So the click events not triggered.
solution
You can try to move your code inner$(function() { // move your codes here });(related question!), or just add console.log($('#movPoster'), $('#movPoster .posters')) before $(".posters").on("click",...) to verify whether the target elements exist or not.
And bind the click events to #movPoster instead of img.posters。
advice
For better performance, you should refactor your code:
Bind the click events to #movPoster instead of img.posters which makes performance worser for too much events listener.
the code $('#movPoster').append(element) in the loop will cause unneccessay repaint for each loop will insert element inner #movPoster. You could rewrite it like this:
var dom = '';
for(var i=0; i<3; i++) {
dom += '<img src="">'
}
$('#movPoster').append(dom) // only insert dom 1 time, not 3 times

Formatting a href link with appendChild, setAttribute, etc

I am attempting to populate a list with href links via javascript.
Here is an example of the html I would like to create:
<li> Complete blood count</li>
Where "#modal-one" displays a pop up.
I have used the following and several other iterations to try and create this dynamically:
<script>
var listItem = [];
function createTestList() {
var tests = results.tests; //an array to tests to populate list
var i;
var j;
for (i = 0; i < tests.length ; i++ ){
listItem[i] = document.createElement("li");
var node = document.createTextNode(tests[i].name);
listItem[i].appendChild(node);
listItem[i].setAttribute("href", "#modal-one");
addOnClick(i);
//var element = document.getElementById("div1");
//element.appendChild(listItem[i]);
document.body.appendChild(listItem[i]);
console.log(listItem[i]);
};
};
function addOnClick(j) { //this is separate to handle the closure issue
listItem[j].onclick = function() {loadModal(j)};
};
</script>
However, this code (and several others) produce:
<li href='#modal-one'>Complete Blood Count</li> //note missing <a>...</a>
It appears there are several ways to achieve this, but nothing seems to work for me...
You are never actually adding in an anchor tag. You are creating a list-item (li), but you are adding an href to that list-item rather than adding an anchor node to it with that href. As such, the browser just thinks you have a list-item with an href attribute.
Consider using the following instead:
<script>
var listItem = [];
function createTestList() {
var tests = results.tests; //an array to tests to populate list
var i;
var j; // Never actually used in function. Consider omitting
for (i = 0; i < tests.length ; i++ ){
// create the list item
listItem[i] = document.createElement("li");
// Create the anchor with text
var anchor = document.createElement("a");
var node = document.createTextNode(tests[i].name);
anchor.appendChild(node);
anchor.setAttribute("href", "#modal-one");
// Set the onclick action
addOnClick(i, anchor);
// Add the anchor to the page
listItem[i].appendChild(anchor);
document.body.appendChild(listItem[i]);
console.log(listItem[i]);
};
};
// Modified "addOnClick" to include the anchor that needs the onclick
function addOnClick(j, anch) { //this is separate to handle the closure issue
anch.onclick = function() {loadModal(j)};
};
</script>
A couple things to note:
I have modified your addOnClick() function because it is the anchor element that needs the onclick, not the list item.
I have added in the creation of an anchor element rather than simply creating a list item and adding the href to that.
I do not see creating a element, change code to:
var aNode=document.createElement("a");
aNode.innerText=tests[i].name;
aNode.setAttribute("href", "#modal-one");
listItem[i].appendChild(aNode);
You can change also click method, to use it on a not on li
function addOnClick(j) {
listItem[j].querySelector("a").addEventListener("click",function(e) {
e.preventDefault();//this prevent for going to hash in href
loadModal(j);
});
};
Okay. I missed the anchor tag. My bad...
Spencer's answer came close, but I had to make few changes to get it work in my instance.
The final working code (and honestly I am not sure why it works) is:
<script>
var listItem = [];
function createTestList() {
var tests = results.tests;
var i;
//var j;
for (i = 0; i < tests.length ; i++ ){
// create the list item
listItem[i] = document.createElement("li");
// Create the anchor with text
var anchor = document.createElement("a");
anchor.setAttribute("href", "#modal-one");
var node = document.createTextNode(tests[i].name);
anchor.appendChild(node);
// Set the onclick action
addOnClick(i);
// Add the anchor to the page
listItem[i].appendChild(anchor);
document.getElementById("demo").appendChild(listItem[i]); //added the list to a separate <div> rather than body. It works fine like this.
console.log(listItem[i]);
};
};
function addOnClick(j) { //this is separate to handle the closure issue
//didn't need the additional code beyond this
listItem[j].onclick = function() {loadModal(j)};
};
</script>
Thanks to all and Spencer thanks for the thoroughly commented code. It helps!!!

Any way to prevent losing focus when clicking an input text out of tinymce container?

I've made this tinymce fiddle to show what I say.
Highlight text in the editor, then click on the input text, highlight in tinyMCE is lost (obviously).
Now, I know it's not easy since both, the inline editor and the input text are in the same document, thus, the focus is only one. But is there any tinymce way to get like an "unfocused" highlight (gray color) whenever I click in an input text?
I'm saying this because I have a customized color picker, this color picker has an input where you can type in the HEX value, when clicking OK it would execCommand a color change on the selected text, but it looks ugly because the highlight is lost.
I don't want to use an iframe, I know that by using the non-inline editor (iframe) is one of the solutions, but for a few reasons, i can't use an iframe text editor.
Any suggestion here? Thanks.
P.S: Out of topic, does any of you guys know why I can't access to tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global var was overwritten by the tinymce select dom element of the page itself. I can't execute a tinyMCE command lol.
Another solution:
http://fiddle.tinymce.com/sBeaab/5
P.S: Out of topic, does any of you guys know why I can't access to
tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global
var was overwritten by the tinymce select dom element of the page
itself. I can't execute a tinyMCE command lol.
Well, you can access the tinyMCE variable and even execute commands.
this line is wrong
var colorHex = document.getElementById("colorHex")
colorHex contains input element, not value.
var colorHex = document.getElementById("colorHex").value
now it works ( neolist couldn't load, so I removed it )
http://fiddle.tinymce.com/DBeaab/1
I had to do something similar recently.
First off, you can't really have two different elements "selected" simultaneously. So in order to accomplish this you're going to need to mimic the browser's built-in 'selected text highlight'. To do this, you're going to have to insert spans into the text to simulate highlighting, and then capture the mousedown and mouseup events.
Here's a fiddle from StackOverflow user "fullpipe" which illustrates the technique I used.
http://jsfiddle.net/fullpipe/DpP7w/light/
$(document).ready(function() {
var keylist = "abcdefghijklmnopqrstuvwxyz123456789";
function randWord(length) {
var temp = '';
for (var i=0; i < length; i++)
temp += keylist.charAt(Math.floor(Math.random()*keylist.length));
return temp;
}
for(var i = 0; i < 500; i++) {
var len = Math.round(Math.random() * 5 + 3);
document.body.innerHTML += '<span id="'+ i +'">' + randWord(len) + '</span> ';
}
var start = null;
var end = null;
$('body').on('mousedown', function(event) {
start = null;
end = null;
$('span.s').removeClass('s');
start = $(event.target);
start.addClass('s');
});
$('body').on('mouseup', function(event) {
end = $(event.target);
end.addClass('s');
if(start && end) {
var between = getAllBetween(start,end);
for(var i=0, len=between.length; i<len;i++)
between[i].addClass('s');
alert('You select ' + (len) + ' words');
}
});
});
function getAllBetween(firstEl,lastEl) {
var firstIdx = $('span').index($(firstEl));
var lastIdx = $('span').index($(lastEl));
if(lastIdx == firstIdx)
return [$(firstEl)];
if(lastIdx > firstIdx) {
var firstElement = $(firstEl);
var lastElement = $(lastEl);
} else {
var lastElement = $(firstEl);
var firstElement = $(lastEl);
}
var collection = new Array();
collection.push(firstElement);
firstElement.nextAll().each(function(){
var siblingID = $(this).attr("id");
if (siblingID != $(lastElement).attr("id")) {
collection.push($(this));
} else {
return false;
}
});
collection.push(lastElement);
return collection;
}
As you can see in the fiddle, the gibberish text in the right pane stays highlighted regardless of focus elsewhere on the page.
At that point, you're going to have to apply your color changes to all matching spans.

Filtering the list of friends extracted by Facebook graph api ( more of a JavaScript/Jquery question than Facebook API question)

Hello there JavaScript and Jquery gurus, I am getting and then displaying list of a facebook user's friend list by using the following code:
<script>
function getFriends(){
var theword = '/me/friends';
FB.api(theword, function(response) {
var divInfo = document.getElementById("divInfo");
var friends = response.data;
divInfo.innerHTML += '<h1 id="header">Friends/h1><ul id="list">';
for (var i = 0; i < friends.length; i++) {
divInfo.innerHTML += '<li>'+friends[i].name +'</li>';
}
divInfo.innerHTML += '</ul></div>';
});
}
</script>
graph friends
<div id = divInfo></div>
Now, in my Facebook integrated website, I would eventually like my users to choose their friends and send them gifts/facebook-punch them..or whatever. Therefore, I am trying to implement a simple Jquery filter using this piece of code that manipulates with the DOM
<script>
(function ($) {
// custom css expression for a case-insensitive contains()
jQuery.expr[':'].Contains = function(a,i,m){
return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
function listFilter(header, list) { // header is any element, list is an unordered list
// create and add the filter form to the header
var form = $("<form>").attr({"class":"filterform","action":"#"}),
input = $("<input>").attr({"class":"filterinput","type":"text"});
$(form).append(input).appendTo(header);
$(input)
.change( function () {
var filter = $(this).val();
if(filter) {
// this finds all links in a list that contain the input,
// and hide the ones not containing the input while showing the ones that do
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
} else {
$(list).find("li").slideDown();
}
return false;
})
.keyup( function () {
// fire the above change event after every letter
$(this).change();
});
}
//ondomready
$(function () {
listFilter($("#header"), $("#list"));
});
}(jQuery));
</script>
Now, This piece of code works on normal unordered list, but when the list is rendered by JavaScript, it does not. I have a hunch that it has to do something with the innerHTML method. Also, I have tried putting the JQuery filter code within and also right before tag. Neither seemed to work.
If anyone knows how to resolve this issue, please help me out. Also, is there a better way to display the friends list from which users can choose from?
The problem is here:
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
Since you're rendering this:
divInfo.innerHTML += '<li>'+friends[i].name +'</li>';
There is no anchor wrapper, the text is directly in the <li> so change the first two lines to look in those elements accordingly, like this:
$(list).find("li:not(:Contains(" + filter + "))").slideUp();
$(list).find("li:Contains(" + filter + ")").slideDown();
You could also make that whole section a bit faster by running your Contains() code only once, making a big pact for long lists, like this:
$(input).bind("change keyup", function () {
var filter = $(this).val();
if(filter) {
var matches = $(list).find("li:Contains(" + filter + ")").slideDown();
$(list).find("li").not(matches).slideUp();
} else {
$(list).find("li").slideDown();
}
});
And to resolve those potential (likely really) innerHTML issues, build your structure by using the DOM, like this:
function getFriends(){
var theword = '/me/friends';
FB.api(theword, function(response) {
var divInfo = $("#divInfo"), friends = response.data;
divInfo.append('<h1 id="header">Friends/h1>');
var list = $('<ul id="list" />');
for (var i = 0; i < friends.length; i++) {
$('<li />', { text: friends[i].name }).appendTo(list);
}
divInfo.append(list);
});
}
By doing it this way you're building your content all at once, the <ul> being a document fragment, then one insertion....this is also better for performance for 2 reasons. 1) You're currently adding invalid HTML with the .innerHTML calls...you should never have an unclosed element at any point, and 2) you're doing 2 DOM manipulations (1 for the header, 1 for the list) after the much faster document fragment creation, not repeated .innerHTML changes.

How do i use jquery on items from $.get(url)

I would like to .get a page and then use jquery to find elements such as links. How do i make $('#blah') search the get data instead of the page?
You should be able to create a dom element from the returned HTML without actually adding it to the document, and then search through that using the jQuery methods:
jQuery.get('/my_url/', function(html_data) {
// If your html_data isn't already wrapped with an HTML object, you may
// need to wrap it like so:
//
// var jQueryObject = $("<div>" + html_data + "</div>");
var jQueryObject = $(html_data);
jQueryObject.find("a.link_class");
// Or, as stated by gregmac below, you could just do the following:
$("a.link_class", html_data);
// or, if wrapping is required:
$("a.link_class", "<div>" + html_data + "<div>");
});
For finding links or a specific element like your example, you can do this:
$.get('test.html', function(data) {
var links = $('a', data); //Use the response as the context to search in
var blah = $('#blah', data);
});
I'm pretty sure you'll be limited to only getting links from a local server/page too.
// create blank array
var links = new Array();
// where should we $.get
var url = '/menus';
$.get(url, function(data) {
// get anchors from the url
links = $(data).find('a');
// loop through all of them
for(i=0; i<links.length; i++)
{
// do something (may alert a lot of links... be prepared)
alert($(links[i]).attr('href'));
}
});
you can simply do this:
$('#result').load('ajax/test.html #container');
only the content of #container will be shown.

Categories

Resources