Fastest way to template and append elements from array - javascript

Using jQuery and Javascript, I am building a chatbox from an array of messages. My current setup is something similar to this:
HTML:
<div id="messageTemplate">
<div class="messageContainer">
<div class="chatMessage"></div>
<div class="chatTime"></div>
<div class="chatActions">
<span class="chatAction deleteMessage">Delete</span>
</div>
</div>
</div>
Javascript:
var template=$('#messageTemplate');
var chatbox=$('#chatbox');
$.each(messageArr,function(index,messageObject) {
var newMessage=$(template.html()); //Make a new DOM
newMessage.find('div.chatMessage').text(messageObject.message);
newMessage.find('div.chatTime').text(messageObject.time);
newMessage.find('div.chatAction.deleteMessage').click(function() {
//Delete message Code
});
//Append newMessage to chatbox
newMessage.appendTo(chatbox);
});
This code works, but after reading this article I found out this is not efficient at all. With 100+ chat messages this starts to slow down.
What's the best way to be doing this? Not looking for code to be written for me, but just a general guide on the best method to template, build the html, append, and add click handlers?

Related

Using Tampermonkey to make an "ignore" button?

I have a script that goes as follows:
// ==UserScript==
// #name TempleOS Ignorer with added Braed Ignoring
// #include *://v3rmillion.net/*
// #grant none
// ==/UserScript==
const ignore = ['189822','695729', '1797404', '1439', '1290050', '941293', '1696676', '440792', '1391811', '114505']
new Array(...document.getElementsByClassName('author'))
.filter(author => ignore.includes(author.firstElementChild.getAttribute('href').match(/=([0-9]+)/)[1]))
.forEach(author => author.parentElement.parentElement.parentElement.remove())
new Array(...document.getElementsByClassName('author_information'))
.filter(author => ignore.includes(author.firstElementChild.firstElementChild.firstElementChild.getAttribute('href').match(/=([0-9]+)/)[1]))
.forEach(author => author.parentElement.parentElement.remove())
There's a div called post-head and I want to add a button to it that takes the user's ID and adds it to the ignore const. I'm fairly new to using Tampermonkey, most of this was done by someone else and I want to make it easier to add users to this ignore list. Something like this. (Very sloppily done, but you get the idea.)
So you can try taking a look at this script I came up with real quick, and then you can try putting it into a UserScript if you'd like. I tried to keep this extremely simple and basic. Just know there are plenty of ways to accomplish this, and this isn't exactly the standard in 2022. Otherwise, you can just copy this JS code into the Dev Tools Console tab on the message board, and it should work for you.
Here is the overall summary of what this is:
HTML is just basic message board HTML, the button is added next at the bottom of the post on the left side
Loops through each .post element on the page, using jQuery .each(), get the idof the post, and the id of the author.
There are probably tons of ways to get the author/post id from the markup on this message board. I simply chose to split() the URL using = and grab the ID at the array index of [2]. Getting the id of the post was just replacing the post_ from the id element on the .post element.
Left in the console.log() so it can be tested, output an alert() notification for button onclick
Appended the markup, which was created using String Literals to the .author-controls elements. I used an <a> element instead of a <button> because I didn't want to add any CSS to make it match.
const ignore = [];
$('.post').each(function() {
let US_pid = $(this).attr('id').replace('post_', '');
console.log('id=post_' + US_pid);
let US_author_id_array = $('.author_buttons > .postbit_find', this).attr('href').split('=')
let US_author_id = US_author_id_array[2];
console.log('author id: ' + US_author_id)
let US_ignore_btn_markup = `Ignore User`;
console.log(US_ignore_btn_markup);
$('.author_buttons', this).append(US_ignore_btn_markup);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="post " style="" id="post_7747511">
<div class="post_head" style="padding-right:5px">
<div class="float_right" style="vertical-align: middle">
<strong><a class="postlink" href="showthread.php?pid=7747511#pid7747511" title=""></a></strong>
</div>
</div>
<div class="post_author">
</div>
<div class="post_content">
</div>
<div class="post_controls">
<div class="postbit_buttons author_buttons float_left">
<span>PM</span>
<span>Find</span>
</div>
<div class="postbit_buttons post_management_buttons float_right">
<span>Report</span>
</div>
</div>
</div>

Get <div> elements without id in GWT

If i have html like this.Is there a way get a text apple between < div class="a" > and send it trought ajax to gwt application ?
<div class="A">
<div class="B">
<img class="icon" src="/images/ico.png" alt="" />
<div class="a">apple</div>
<div class="b">bannana</div>
</div>
</div>
and i have JavaScript like this :
$function(){
$('.B .icon').click(function(){
$(this).closest('.B').addClass('marked');
});
}
I would add this as a comment if I had enough reputation as I'm confused about your structure and question in general. Apologies if I don't understand correctly, but if you're typically traversing back up the DOM structure to the parent in your case could you use find to grab the element? .html() will grab the current content of said element.
$(function() {
$('.icon').click(function(){
var html = $(this).closest('.B').find('div.a').html();
// Do what you want with the contents. Simple alert as example.
alert(html);
});
});
This should get you what you're looking for. Please mark it as the answer if you find it matches what you need.
I'd suggest using .parent() rather than .closest('.B') if your elements will always be in the order you've posted and if further searching of the ancestor elements isn't needed.

JQuery find element in script

I am really not great at web stuff, so I am apologizing in advance for a potentially poor explanation of my problem.
Basically, I have a webpage which utilizes the handlebars js templating. Unfortunately, this means that many of my div elements are contained within javascript tags like the following:
<script type="text/x-handlebars" data-template-name="index">
<div class="row intro">
......
</div>
<div class="descript">
.....
</div>
</script>
My intent is to grab one of these div elements using jquery.find(), but from what I understand, the html within the script tags is not treated as part of the dom...so jquery does not see it as a dom element. I was wondering if there is any other way I could go about this. Some more code is included.
Here is another more explicit explanation in case the one I gave above was a little muddled: I am working on a personal website and would like to embed a project I have been working on in unity3d, but I need to add/remove elements based on whether or not the client has the unity3d web player installed. Normally I would get a particular element with
var $missingScreen = jQuery("#unityPlayer").find(".missing");
where 'missing' is simply an element inside unityPlayer which displays a link if the client does not have unity3d. I am also using some javascript templating to make my site look pretty and as a result, I have this problem:
<script type="text/x-handlebars" data-template-name="index">
<div class="row intro">
<div class="intro-text">Hi, I'm *****</div>
</div>
<div class="descript">
<p>
Here's a Project I have been working on in case I am of interest to you:
</p>
</div>
<div class="content">
<div id="unityPlayer">
<div class="missing">
<a href="http://unity3d.com/webplayer/" title="Unity Web Player. Install now!">
<img alt="Unity Web Player. Install now!" src="http://webplayer.unity3d.com/installation/getunity.png" width="193" height="63" />
</a>
</div>
</div>
</div>
<p class="footer">« created with Unity »</p>
</script>
Jquery cannot access the missing element. Is there any way to do this? Thanks for any help you can give me and sorry again for my inexperience.
EDIT* some people might want to know: here is how I determine whether or not to show the missing div. Another note; everything works fine if I remove the script tags...it is only when I put html within the script tags that it becomes inaccessible to jquery.
jQuery(function() {
var $missingScreen = jQuery("#unityPlayer").find(".missing");
$missingScreen.hide();
u.observeProgress(function (progress) {
switch(progress.pluginStatus) {
case "missing":
$missingScreen.find("a").click(function (e) {
e.stopPropagation();
e.preventDefault();
u.installPlugin();
return false;
});
$missingScreen.show();
break;
case "installed":
$missingScreen.remove();
break;
case "first":
break;
}
});
u.initPlugin(jQuery("#unityPlayer")[0], "temmp.unity3d");
});
Instead of
var $missingScreen = jQuery("#unityPlayer").find(".missing");
I would try out
var $missingScreen = jQuery("#unityPlayer").children(".missing");
or
var $missingScreen = jQuery("#unityPlayer .missing");
Don't forget the space between Player and .missing!
I hope it works.

passing variables on jquery

just having some issues with this jQuery thing.
What i'm trying to do is:
i have some audio control buttons that look like this:
<p>Play audio</p>
but there are too many on the page so i'm trying to optimise the code and make a little function that checks for the div id on the button and adds tells the player what track to play.
so i've done this:
<div id="audioControlButtons-1">
<div class="speaker"> </div>
<div class="play"> </div>
<div class="pause"> </div>
</div>
<script>
$(document).ready(function() {
$("[id^=audioControlButtons-] div.play").click(function() {
var id = new Number;
id = $(this).parent().attr('id').replace(/audioControlButtons-/, '');
//alert(id);
player1.loadAudio(id);
return false;
});
});
</script>
my problem is:
the id is not passing to the the player1.loadAudio(id)
if i hardcode player1.loadAudio(1)
it works! but the moment i try to pass the variable to the function it doesn't work...
however if you uncomment the alert(id) thing you will see the id is getting generated...
can someone help?
cheers,
dan
I think I see your problem. The variable id is a string. Try;
player1.loadAudio(parseInt(id));
Yah and the initialise line isn't necessary. Just use;
var id = $(this).parent().attr('id').replace(/audioControlButtons-/, '');
I'm actually kind of confused with your example because you originally have this:
<p>Play audio</p>
but then you don't reference it again. Do you mean that this html:
<div id="audioControlButtons-1">
<div class="speaker"> </div>
<div class="play"> </div>
<div class="pause"> </div>
</div>
Is what you are actually creating? If so, then you can rewrite it like this:
<div class="audio-player">
<div class="speaker"> </div>
<div class="play" data-track="1"> </div>
<div class="pause"> </div>
</div>
Then in your script block:
<script>
$(document).ready(function() {
$(".audio-player > .play").click(function() {
var track = $(this).data('track');
player1.loadAudio(+track);
return false;
});
});
</script>
So a few things are going on here.
I just gave your containing div a class (.audio-player) so that it's much more generic and faster to parse. You don't want to do stuff like [id^=audioControlButtons-] because it is much slower for the javascript to traverse and parse the DOM like that. And if you are going to have multiples of the same element on the page, a class is much more suited for that over IDs.
I added the track number you want to the play button as a data attribute (data-track). Using a data attribute allows you to store arbitrary data on DOM elements you're interested on (ie. .play button here). Then this way, you don't need to this weird DOM traversal with a replace method just to get the track number. This saves on reducing unnecessary JS processing and DOM traversing.
With this in mind now, I use jQuery's .data() method on the current DOM element with "track" as the argument. This will then get the data-track attribute value.
With the new track number, I pass that along into your player1.loadAudio method with a + sign in front. This is a little javascript trick that allows you to convert your value into an actual number if that is what the method requires.
There are at least a couple of other optimizations you can do here - event delegation, not doing everything inside the ready event - but that is beyond the scope of this question. Hell, even my implementation could be a little bit optimized, but again, that would require a little bit more in depth explanation.

How can I make this Javascript code unobtrusive?

<div id="detailed">
#foreach (var item in Model.Result.Items)
{
<div id="movie_#(movie.UserMovieID)" class="movie border-gray">
<!-- Some html code -->
</div>
<script type="text/javascript">
new InitMovieWicket(#(MvcHtmlString.Create(movie.ToJSon())),"movie_#(movie.UserMovieID)");
</script>
}
</div>
I am getting list of movie objects from ASP.NET MVC server-side and generating html like above. As you can see , I am also initializing javascript wickets for each of these movies by using movies's JSON data and script tags.
I want to remove these script tags from html and javascript code to be unobtrusive but I dont know how to do that because for each movie to create wicket I need JSON data and without rendering time script tags I cannot see a way to do this. Do you have any idea ? Thanks..
Update
I want want my html code to look like this.
<div id="detailed">
#foreach (var item in Model.Result.Items)
{
<div id="movie_#(movie.UserMovieID)" class="movie border-gray">
<!-- Some html code -->
</div>
}
</div>
And I want my Javascript code to look like this.
$(document).ready(function() {
//init all movie wickets
});
A potentially better way, for users which don't have JavaScript enabled but still want to see movie details for each item, would be to actually render those details as HTML elements, and then hide them if JavaScript is available.
For example, you would render your HTML to look something like:
<div id="detailed">
<div id="movie_#1234" class="movie border-gray">
<div class="wicketData title">Some title</div>
<div class="wicketData year">Year</div>
<div class="wicketData synopsis">Some other stuff</div>
</div>
</div>
And then iterate through the elements and replace divs with anything you like:
$(doument).ready(function() {
// get the parent div
var $detailedParent = $("#detailed");
// get a list of all movie class divs
var $items = $detailedParent.find(".movie");
$.each($items, function(i) {
// get the movie div
var movie = $items[i];
// get all wicket data
var data = movie.find(".wicketData");
// prepare the JSON data using DOM
var movieData = {
title = data.children("title").text(),
year = data.children("year").text(),
synopsis = data.children("synopsis").text()
};
// remove or hide dummy elements
data.remove();
// init your wicket
new InitMovieWicket(movieData, movie.attr('id'));
};
});
This will allow users without JavaScript to get a bit degraded presentation, but all the data will still be there.
here's how I'd do it:
Put only one script tag in the <head></head> part where you initialize an JSON array with all the movies in it (on the server side) like:
var movies = '[{"userMovieID": "123", ...}, {"userMovieID": "432", ...}]';
when the document is ready, you should start building the widgets by first parsing the json array and then iterating through the array of movies, then create a Widget for every movie and insert
<div id="movie_#(movie.UserMovieID)" class="movie border-gray">
<!-- Some html code -->
</div>
to your <div id="detailed">..</div>, maybe you want to use Javascript Templates like jquery-tmpl
The best solution would be if your widget class "InitMovieWicket" creates the <div id="movie_"..>...</div> entry.

Categories

Resources