Adding an element dynamicaly to a HTML page - javascript

Trying to create a new list element every time a key press is detected. Originally I had this written down--
<ul class='text-box'> <strong>THIS IS SPARTA</strong>
</ul>
<script>
keycheck = 0;
$(document).keydown(function()
{
$('.text-box').text('Number of keypress are: '+keycheck);
keycheck=keycheck+1;
$('<p>').text(keycheck).appendTo('.text-box');
});
</script>
but this only created TWO line on keypress that updated with the number of clicks.
BUT removing the line "$('.text-box').text('hello '+keycheck);" gives me what I want.
I would like like to know why it did that, since I just started learning js I feel this would be something valuable.

If you do
$('.text-box').text('Number of keypress are: '+keycheck);
you replace all of the contents of .text-box with the given text. And "all of the contents" include the content that was added last time!
So you need append(...) instead of text(...). That's it.
However, if you want to make a list, you should use list items (<li>), not plain text and <p>. A better solution looks like this:
keycheck = 0;
$(document).keydown(function() {
$('.text-box').append('<li>Number of keypresses are: ' + keycheck + '</li>');
keycheck = keycheck + 1;
$('<li>').text(keycheck).appendTo('.text-box');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class='text-box'>
<li>THIS IS SPARTA</li>
</ul>

Related

How to dynamically get the dynamically created ID in a li

I've been trying to learn js (and a tad of jquery) and I have run into two difficulties when trying to find a way to combine solutions that I find.
Just a little warning that this code is a mix of a few tutorials that I have recently done. I am very new to js.
So I start with a basic html with a few li.
<body>
<ol id="liste">
<li class="active">
</li>
<li>
</li>
<li>
</li>
</ol>
<div id="main_ima">
</div>
<script src="js/main.js"></script>
</body>
I want to create ids for each "li" so in my main.js I add this:
var idVar = $("#liste").find("li").each(function(index){
$(this).attr("id","num-li-"+index);
});
This works great so far. Everytime I add a new li, it gets a new id. I also put it into a var because I will need to use it later.
In th console, If I type idVar, it gives me the whole list of li. If I type idVar[3]. it only gives me the li associated to the [3]. Perfect.
Now I want to get something to appear when one of the li is clicked. For example, I will use the [3]. So I add this to my main.js
var imaContainer = document.getElementById('main_ima')
var listed = document.getElementById('liste');
idVar[3].addEventListener("click", appar);
function appar(){
$(idVar[3]).addClass("active").siblings().removeClass("active");
var imaSel = new XMLHttpRequest();
imaSel.open('GET', 'https://domain.link.to.file.json');
imaSel.onload = function() {
var imaLo = JSON.parse(imaSel.responseText);
renderHTML(imaLo);
};
imaSel.send();
};
function renderHTML(data) {
var htmlS = "";
for (i = 0; i < data.length; i++) {
htmlS += "<p>" + data[i].name + " is a " + data[i].species + ".</p>";
}
imaContainer.insertAdjacentHTML('beforeend', htmlS);
}
Just a side note, I added the add/remove "active" class for CSS.
So when I click the li[3], it works almost as expected. The only thing is when I reclick [3] it produces the result a 2nd time. And again, if I click it a 3rd time, it produces the result a 3rd time, without remove the past results. (which is not totally what I want. Just the 1st result would be better.)
But that is not the main problem I am facing.
I would like the [number] to be dynamically detected, based on the id of the clicked li. I could, in a very ugly way, copy and past this code for every [number] I have. and it would work. But then, what if I want to add more li elements, I would need to add more copy and paste of the above code, giving me possibly huge files for nothing. This is surely not the best way, although it would work.
I'm sure this can be done dynamically.. but that is mostly why I am here. :)
Afterwards, once the dynamic has been added to the clicked li, I would also like the link to be changed dynamically based on the li id. For example, instead of :
imaSel.open('GET', 'https://domain.link.to.file.json');
something like:
imaSel.open('GET', "https://domain.link.to.file" + var +".json");
the var being equal to the [3] number of the clicked li.
In this case, when I try to add a var with a for loop, I always get the "var = max.length" instead of the "var = [id of clicked item]".
So there you have it. Do you need more details?
This is my first JS and/or Jquery try. I've been playing with it for a few days but when I search for answers, when I implement the "solutions" it alwas gives me some new problem. So I am showing you the code that is the closest, IMO, to what I am looking for.
Hopefully, I am not too far away of somehting that works and is not as big as my solutions. :)
Thanks for your time and all help is appreciated.
Here are some suggestions:
You don't need to assign id attributes to your li. You actually never need that id. This will work just as well (note also the > in the selector which makes the find call unnecessary):
var $li = $("#liste > li");
Already now you can address each of the li as $li[3], although that is not the "best practise". Better is $li.get(3). I also like the convention to start the variable with $ when it is the result of a jQuery selection. It gives a clue that you can apply jQuery methods to it.
You don't need to assign a click handler to each li separately. With jQuery on (instead of the native addEventListener) you can assign one event handler for all of them at once.
$li.on('click', apar)
The callback you define for on will have this set to the particular li element that has been clicked, so you can do:
$(this).addClass("active").siblings().removeClass("active");
... without any array lookup.
jQuery offers easy functions for several types of HTTP requests, so you don't need to use XMLHttpRequest. In fact, there is one specifically for getting JSON, so you don't even have to parse the response:
$.getJSON('https://domain.link.to.file.json', renderHTML);
The jQuery index() method can give you the sequence number of that li:
$.getJSON('https://domain.link.to.file' + $(this).index() + '.json', renderHTML);
To replace the inner HTML of a certain element, the jQuery html method can be used:
$('#main_ima').html(htmlS);
Note also how you don't need the DOM native getElementById method, jQuery can look that up for you with the short $('#main_ima').
Example
Here is a working example with a fake JSON serving server:
$("#liste > li").on('click', apar);
function apar() {
$(this).addClass("active").siblings().removeClass("active");
$.getJSON('https://jsonplaceholder.typicode.com/posts/'
+ (1+$(this).index()), renderHTML);
}
function renderHTML(data) {
// This particular JSON request returns an object with body property
var htmlS = data.body;
$('#main_ima').html(htmlS);
}
// On page load, click on the first `li` to automatically load the data for it
$('#liste > li:first').click();
#liste { width: 40px }
.active { background: yellow }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ol id="liste">
<li class="active">load 1</li>
<li>load 2</li>
<li>load 3</li>
</ol>
<div id="main_ima"></div>
The following answers your main concern, how to dynamically get the ID with jquery:
$('.listen-to-me').click(function() { //Add event listener to class
var elementId = $(this).attr('id'); //Get the 'id' attribute of the element clicked
var idNumber = elementId.substring(elementId.indexOf("-") +1); //Get the index of the "-" in the string, and then cut everything prior
alert(idNumber); //The final result
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li id="test-1" class="listen-to-me">1</li>
<li id="test-2" class="listen-to-me">2</li>
<li id="test-3" class="listen-to-me">3</li>
<li id="test-4" class="listen-to-me">4</li>
<li id="test-5" class="listen-to-me">5</li>
</ul>

replace content of div with another content

I've been building a list of links, all of which should change the content of a div to another specific content (about 4 lines of stuff: name, website, contact etc.) upon a click.
I found this code:
<script type="text/javascript">
function ReplaceContentInContainer(id,content) {
var container = document.getElementById(id);
container.innerHTML = content;
}
</script>
and used it in such a way:
<li class="pl11">
superlink')">Pomorskie</a>
</li>
And it doesn't work as I expected.
It changes hyperlinks text from 'Pomorskie' to 'superlink'.
The plain text works just fine but I need links.
here's the http://xn--pytyfundamentowe-jyc.pl/projektanci/kontakty-p/ (only two of them show anything)
But after trying all of your recomendations, I think I'd jump to different divs with #links, cause nothing worked with this :/
Thanks a lot for trying, and cheers :)
Just as a completely sideways look at this, I'd suggest avoiding the nesting weirdness / complexity, and reducing the problem down.
Setup the content in a hidden (ie. <div id="replacements">...</div>) Grab the innerHTML from the node you want, and be done with it.
Much easier to get replacement content from non-devs that way too, kinda works great if you're in a team.
// Probably better in a separate helpers.js file.
function replaceContentInContainer(target, source) {
document.getElementById(target).innerHTML = document.getElementById(source).innerHTML;
}
Control it with: (lose that href=javascript: and use onClick, better as an event handler, but for brevity I'll inline it as an onClick attribute here, and use a button.)
<button onClick="replaceContentInContainer('target', 'replace_target')">Replace it</button>
We have our target somewhere in the document.
<div id="target">My content will be replaced</div>
Then the replacement content sits hidden inside a replacements div.
<div id="replacements" style="display:none">
<span id="replace_target">superlink</span>
</div>
Here it is in JSBin
Improve the dynamic nature of this by using Handlebars or another nice JS templating library, but that's an exercise for the OP.
edit: Note, you should also name functions with a leading lowercase letter, and reserve the leading uppercase style for Class names e.g. var mySweetInstance = new MySpecialObject();
The quotes are mismatched! So when you click you are getting a JavaScript error.
The browser sees this string:
href="javascript:ReplaceContentInContainer('wojewodztwo', 'superlink')">Pomorskie<
as:
href="javascript:ReplaceContentInContainer('wojewodztwo', '<a href="
Chnage the " inside to #quot;
<li class="pl11">
Pomorskie
</li>
Example fiddle.
Also note, using the href tag for JavaScript is a BAD practice.
You've got a problem with nested quotes. Take a look in your DOM inspector to see what the HTML parser built from it! (in this demo, for example)
You either need to HTML-escape the quotes inside the attribute as " or ", or convert them to apostrophes and escape them inside the JS string with backslashes:
<a href="j[…]r('wojewodztwo', '<a href="http://address.com">superlink</a>')">…
<a href="j[…]r('wojewodztwo', '<a href=\'http://address.com\'>superlink</a>')">…
See working demos here and here.
Better, you should use a onclick attribute instead of a javascript-pseudo-url:
<a onclick="ReplaceContentInContainer('wojewodztwo', …)">Pomorskie</a>
or even a javascript-registered event handler:
<li class="pl11">
<a id="superlink">Pomorskie</a>
</li>
<script type="text/javascript">
function replaceContentInContainer(id,content) {
var container = document.getElementById(id);
container.innerHTML = content;
}
document.getElementBId("superlink").onclick = function(event) {
replaceContentInContainer('wojewodztwo', 'superlink');
event.prevenDefault();
};
</script>
(demo)

while loop in Javascript with concatenation

I'm trying to make a while-loop in my Javascript.
So far, I've got this:
<script>
$(document).ready(function(){
var i=0;
while (i<9999) {
$(".add_new_item_field" + i).hide();
$(".add_new_item_button" + i).click(function(){
$(".add_new_item_field" + i).slideToggle("slow");
});
i++;
}
});
</script>
Goal is to make this work:
<div class="add_new_item_button1"></div>
<div class="add_new_item_button2"></div>
<div class="add_new_item_button3"></div>
...
<div class="add_new_item_field1">Show me something</div>
<div class="add_new_item_field2">Show me something</div>
<div class="add_new_item_field3">Show me something</div>
...
But for some reason, it's not working. Am I missing something here?
Your problem is that the concatenation in the line $(".add_new_item_field" + i).slideToggle("slow"); happens when you click one of the divs. Yet, the loop that had set up the handlers was run long ago then and i already has a value of 9999. Use a closure as #David demonstrated to avoid this.
However, I feel this is the wrong approach. Setting up 10000 click handlers, and executing 20000 jQuery selection does make your page very, very slow. Use one common class for the button, and one common class for the fields. If you can't depend on a certain document order, give them unique ids to refer to each other - but not classes.
Then hide all the fields with one single line of CSS, and use event delegation for the buttons to fire 1 single function that looks up the field by id from the data attached to the clicked button.
<style>
.add_new_item_field { display:none; }
</style>
<!-- placing the stylesheet here also avoids flickering.
Even better would be of course if it was written dynamically by JS, for not
hiding the fields in clients that do not support JavaScript -->
<script src=/"jquery.js"></script>
<script>
jQuery(function($) {
$(document).on("click", ".add_new_item_button", function(e) {
var id = "field"+$(this).data("field");
$('#'+id).show();
});
});
</script>
<div class="add_new_item_button" data-field="1"></div>
<div class="add_new_item_button" data-field="2"></div>
<div class="add_new_item_button" data-field="3"></div>
...
<div class="add_new_item_field" id="field1">Show me something</div>
<div class="add_new_item_field" id="field2">Show me something</div>
<div class="add_new_item_field" id="field3">Show me something</div>
...
I’m guessing that i is not what you expect it to be in the handler, because when the handler executes i has already maxed out to 9999. To fix that, you need to bring the variable into the handler closure, something like:
var i=0;
while (i<9999) {
$(".add_new_item_field" + i).hide();
$(".add_new_item_button" + i).click((function(i) {
// i is now saved in this closure
return function() {
$(".add_new_item_field" + i).slideToggle("slow");
};
}(i)));
i++;
}
Sidenote: I’m not really sure this is the best way to solve your actual task here though, looping and attaching 9999 event handlers seems unnecessary...
I think you are using the class the wrong way. You can assign a click handler to all objects of the same class. But you are trying to use the class specifier as an ID and are trying to assign the handler to each seperate object. You wouldn't do the same for the behavior and layout of a link/url? (link) would you?
Read this: jQuery: How do I add a click handler to a class and find out which element was clicked?
You want to setup the handler for a class, specify your divs as that class. I not abusing the class specifier as some sort of ID.
You are creating a closure in your click handler which captures the i variable, not it's value. This means all the click functions will contain the value 9999 which the value of the variable at the end of the while loop.
You can fix it by creating a function that set's the click handler.
var setClick = function(index) {
$(".add_new_item_button" + index).click(function(){
$(".add_new_item_field" + index).slideToggle("slow");
}
}
And use it in your while loop.
while (i<9999) {
$(".add_new_item_field" + i).hide();
setClick(i);
i++;
}
Now the value of i is correctly captured by your click handler

Please cast an eye over my beginners javaScript

I am working on an interface with four main parts:
When a category link is hovered, the projects not in this category are darkened (this seems to be working ok)
When a category link is clicked, the projects not in this category are hidden (seems also to be ok)
The browser window size is detected and a style sheet is chosen to fit. I.e. for older screen or mobile. Go ahead and resize the browser window.
When the browser window is narrow there is an additional script to scroll down to the "main" div.
<div id="container">
<div id="inner-container">
<div id="tag-selector">
<ul>
<li class="all">ALL PROJECTS</li>
<li class="graphic-design">graphic design</li>
<li class="logo-design">logo design</li>
<li class="photography">photography</li>
<li class="web-development">web development</li>
<li class="web-design">web design</li>
</ul>
</div>
<div id="main" role="main">
<p class="items">There are x items in this category</p>
<p class="selected">No category selected</p>
<p class="clicked">No category clicked</p>
<section class="graphic-design">
<p>graphic-design</p>
</section>
<section class="logo-design graphic-design">
<p>logo-design</p><p> graphic-design</p>
</section>
<section class="logo-design graphic-design"><p>etc</p>
</section>
</div>
<footer> </footer>
then here's the javascript. Sorry if it's a bit long. It should be easy enough to read I hope.
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(document).ready(function(){
var xwidth =$(window).width();//get width of user window
all_projects_showing_text="All projects showing. There are " + n + " projects, in " + t + " categories.";
adjustStyle(xwidth);
$("p.items").text(all_projects_showing_text + " Width=" + xwidth);
$(".all").addClass("selected");
tag="all"
});
</script>
<script>
var n = $("section").length;//number of section boxes on page
var t = $("#tag-selector li").length;//categories
t--;
$("#tag-selector li").click(function() {//clicking section filter li
$("#tag-selector li").removeClass("selected");//removes all filtered class
$(this).addClass("selected");//then adds it to the chosen link (li), showing it as current
tag=$(this).attr("class");//var tag is the class name of the chosen link, i.e. category
var split = tag.split(' '); // this splits the class string and puts each item in an array
tag = split[0];//this chooses the first item of the array, hence not including the hilite class
var numItems = $('.'+tag).length
var numItems=numItems-1;//correct for real number
if (tag!="all"){//if the all link is not picked
$("section").hide();// hide all the boxes
$("#main ."+tag).fadeIn();//show all the boxes with the tag class
if(tag=="graphic-design"){
tag="Graphic design"
}
else if(tag=="logo-design"){
tag="Logo design"
}
else if(tag=="photography"){
tag="Photography"
}
else if(tag=="web-development"){
tag="Web development"
}
else if(tag=="web-design"){
tag="Web design"
}
$("p.items").text(numItems+" " +tag+ " projects");
$("p.selected").text(tag +" selected.");
$("p.clicked").text(tag +" selected.");
}
else{
$("section").fadeIn();//else show all the boxes
$("p.items").text(all_projects_showing_text);// all_projects_showing_text at onReady
}
});
</script>
<script>
$("#tag-selector li").hover(function () {
hovered_link=$(this).attr("class");//get the class of the category being hovered
var split = hovered_link.split(' '); // this returns an array
hovered_link = split[0];//remove any other classes apart from the first i.e. remove hilite
if (tag=="all"){// if All are shown
if(hovered_link!="all"){
$("section").not("."+hovered_link).addClass("section_darkened");//darken section which does not correspond with hovered category link
$("section").not(".section_darkened").addClass("outerglow");//add glow to not darkened sections
}
}
else{
}
if (tag==hovered_link){// if the projects are already filtered by this category, say so on hover
$("p.selected").text(tag +" already selected.");
}
else{
var numItems = $('.'+hovered_link).length
var numItems=numItems-1;//correct for real number
$("p.selected").text("Click to see only "+hovered_link+ " projects. (" +numItems+ " projects)" );
}
$(this).addClass("hilite");//hilite on hover over link
}, function () {
$(this).removeClass("hilite");
$("p.selected").text("...");
$("section").removeClass("section_darkened");//darken categories not in the selected category
$("section").removeClass("outerglow");//give the selected category items a glow
});
</script>
<script>
$(function() {
$(window).resize(function() {
adjustStyle($(this).width());
});
});
function adjustStyle(width) {
width = parseInt(width);
if (width < 600) {
$("#tag-selector li").click(function() {// SCroll function for handhelds
$('html,body').animate({
scrollTop: $("#main").offset().top},
'slow');
});
$("#size-stylesheet").attr("href", "css/nav-style-narrow.css");//style sheet for handhelds
} else if ((width >= 440)&&(width < 1040)){
$("#size-stylesheet").attr("href", "css/nav-style-med.css");
} else {
$("#size-stylesheet").attr("href", "css/nav-style-wide.css");
}
}
</script>
If you've gotten this far and had a look, thanks!
So my questions are;
Am I missing break; in my loops anywhere? Not too sure how to use break.
When my CSS file is chosen, there is a flash of the first style before it changes. Is there a way to avoid this?
When the browser is at the narrowest style sheet, and I click on my link, I have problems scrolling back up again afterwards. help?! :-)
Any glaring mistakes or omissions that would make this easier?
I start to feel like I have a lot of script on my one page. Maybe I should put it in a separate file. Would that help?
Is it ok to post multiple questions like this or should they be individual?
Thanks in advance for anyone who has a look.
Answer regarding break:
break stops the execution of the current loop or switch. You should use it in loops in order to stop a loop before the end of the current iteration, or on a condition that is not checked in the loop statement itself. You should use it at the end of a case in a switch block in order not to execute subsequent cases.
In your specific code there don't seem to be any loops or switches, so no place for any break anywhere.
OK, I'll bite, though I'm not going to try to offer a comprehensive list:
There are various ways to deal with the CSS flash issue. The easiest is to hide everything until you've loaded the correct stylesheet, then show everything once it's loaded.
Yes, in general it's always a good idea to put Javascript in separate files - it just makes managing your code easier, especially if you want to reuse any of it on multiple pages.
You're missing a lot of var statements, e.g. for all_projects_showing_text. This is syntactically correct, but a Bad Idea in Javascript, because it makes those variables global, attaching them to the window object. If you need a global variable, you should still declare it with var.
I'm not seeing any place where break would be appropriate, or even possible. You generally use break within a for or while loop to stop looping; I'm not seeing any loops like that in your code. JQuery code often uses .each() instead, or just loops implicitly through all the items in the selection; I rarely see break in code using jQuery, though there are of course times when it might be appropriate.
It's often a good idea to either cache or chain jQuery selectors. For example,
$("section").removeClass("section_darkened");
$("section").removeClass("outerglow");
could be
var $section = $section;
$section.removeClass("section_darkened");
$section.removeClass("outerglow");
or
$("section")
.removeClass("section_darkened")
.removeClass("outerglow");
or even (in this case, since .removeClass() can remove several classes at once):
$("section")
.removeClass("section_darkened outerglow");
Your long else if section starting if(tag=="graphic-design"){ could be better structured as a map + lookup:
var tagTitles = {
"graphic-design":"Graphic design",
"logo-design":"Logo design",
// etc
};
tag = tagTitles[tag];
break is not a function. It's a statement, so you don't add parenthesis.
The page loads before the css is choosen. If you want to target different screen sizes, you could take a look at css3 media queries. Adding the styles at the beginning of the page should work without flickering. You could still use js to choose styles as backup method.
I think you are adding a new click handler on every resize event! That's a lot of animations running on a click, try to set the handler only once.
Missing var, as already mentioned by nrabinowitz. Indentation could be better / more consistent.
JS in Separate files is better.
cacheable by clients -> page gets faster after first visit
reusable by different pages
easier to manage (version control)
Single (well researched) questions are generally better.

JQuery Copy text & paste into textarea

I've created a javascript function that will take a hidden span, copy the text within that span and insert it into a single textarea tag on a website. I've written a function in JavaScript that does this (well, kinda, only after a few clicks), but I know there's a better way - any thoughts? The behavior is similar to a Retweet for twitter, but using sections of a post on a blog instead. Oh, and I'm also calling out to jquery in the header.
<script type="text/javascript">
function repost_submit(postID) {
$("#repost-" + postID).click(function(){
$("#cat_post_box").empty();
var str = $("span#repost_msg-" + postID).text();
$("#cat_post_box").text(str);
});
}
</script>
Based on the comment in your question, I am assuming you have something like this in your HTML:
copy post
And I am also assuming that because you are passing a post ID there can be more than one per page.
Part of the beauty of jQuery is that you can do really cool stuff to sets of elements without having to use inline Javascript events. These are considered a bad practice nowadays, as it is best to separate Javascript from your presentation code.
The proper way, then, would be to do something like this:
<a href="#" id='copy-5' class='copy_link'>copy post</a>
And then you can have many more that look similar:
<a href="#" id='copy-5' class='copy_link'>copy post</a>
<a href="#" id='copy-6' class='copy_link'>copy post</a>
<a href="#" id='copy-7' class='copy_link'>copy post</a>
Finally, you can write code with jQuery to do something like this:
$(function() { // wait for the DOM to be ready
$('a.copy_link').click(function() { // whenever a copy link is clicked...
var id = this.id.split('-').pop(); // get the id of the post
var str = $('#repost_msg-' + id); // span not required, since it is an ID lookup
$('#cat_post_box').val(str); // empty not required, and val() is the proper way to change the value of an input element (even textareas)
return false;
});
});
This is the best way to do it even if there is only one post in the page. Part of the problem with your code is that on the first click it BINDS the function, and in the subsequent clicks is when it finally gets called. You could go for a quick and dirty fix by changing that around to just be in document.ready.
$("#repost-" + postID).click(function(){
$("#cat_post_box").val(''); // Instead of empty() - because empty remove all children from a element.
$("#cat_post_box").text($("#repost_msg-" + postID).text());//span isn't required because you have and id. so the selector is as efficient as it can be.
});
And wrap everything in a $(document).ready(function(){ /Insert the code here/ }) so that it will bind to $("#repost-" + postID) button or link when the DOM is loaded.
I had a problem with Paolo's example when I clicked on the link the text that appeared in #cat_post_box was "object Object". Once I added ".text()" to the end of that statement I worked.
var str = $('#repost_msg-' + id).text();
Thanks for you example Paolo!

Categories