Cannot use css() on dynamically created div in custom plugin - javascript

I have recently taken up learning how to make a jQuery plugin. This is my first attempt at just creating something that is very simple. I am still relatively new to jQuery and have come into a bit of a bind when it comes to selecting dynamically created content. In this scenario I am attempting to select a div I created within the plugin.
I have made a jsFiddle here.
I have perused many posts about selecting dynamically created div's and most of them are solved either using on or a callback function. And I am not sure that those can be applied in this situation.
I think the issue occurs at this point in the code:
$element.append("<div class=\"gifLoader\"></div>");
$gifLoader = $element.find('.gifLoader');
$gifLoader.css("bacground-image", "url(\"" + plugin.settings.gifSrc + "\")");
plugin.settings.callback.call(this);
Is there some way I can use a callback function like you would with methods like fadeTo? Also, if anyone cares to comment. I would really appreciate some feedback on the layout of my plugins. I don't fully comprehend what it is I am doing when making a plugin, I am just hoping to learn how to use Javascript and jQuery without the coding looking so clunky. (Before I just had anonymous functions within anonymous functions)

You have one typo, and one error: bacground-image => background-image, and src() => url()
Change
$gifLoader.css("bacground-image", "src(\"" + plugin.settings.gifSrc + "\")");
To
$gifLoader.css("background-image", "url(\"" + plugin.settings.gifSrc + "\")");

When you are creating a dynamic element using javascript or jquery, that element does not reflect in your DOM straight away. If you take a look into the HTML that is generated using the developer tools you'd find before: and after: tags appended with your HTML.
The best way to realize the problem is to either specify the background-image when you are creating the div like:
$("#element").append("<div class='gifLoader' style='background-image: url('" + plugin.settings.gifSrc + "')'></div>");
Otherwise if you are looking to change the background-image on some kind of user interaction, you can go for this:
$("#element").append("<div class='gifLoader' id='someID' someEvent='transform(someID)'></div>");
And then have function transform() defined somewhat like:
function transform(elementID) {
$("#" + elementID).css("background-image", "some image URL");
}

Related

Javarscript issues with dynamic tooltipster

I'm using Tooltipster which seems to be a nice jquery plugin.
Regardless I need to have my tooltips dynamic, which I don't believe should be that difficult. However I wrote a script and maybe it's because I'm tired or I don't know the simplest of javascript. Probably a combination of both.
I can't seem to get around this particular error. TypeError: $(...).tooltipster is not a function.
Here is the basic javascript code:
$("img#box_image[data-img-number]").hover(function(e) {
e.preventDefault();
i = $(this).attr("data-img-number");
var w = "http://trailerbrokerimages.s3.amazonaws.com/pics/" + i ;
window.console.log('before tool');
window.console.log('before tool ' +w);
tool(w);
});
var tool = function (w) {
$('.tooltip_two').tooltipster({content: $('<span><img style="height:191px; width:256px;"src="http://trailerbrokerimages.s3.amazonaws.com/pics/'+w+'" /></span>')});
An example of the code can be found at http://www.trailerbroker.com/go/listings/view/219
I suspect it's lame mistake on my part, thanks.
You have the same id box_image for multiple elements.
I understand that you're trying to make it unique by appending the data-img-number, but this won't work, as there's no way you can do this at run time unless your explicitly specifying different hover handlers.
Instead you could attach the hover handler to a class.
Add a class="box_image" to your <img /> elements and attach the hover as follows,
$(".box_image").hover(//rest of your code here//)
This should give you the desired functionality.
I solved this problem by using twitter bootstrap popover. Don't waste your time with tooltipers.

About good practices for creating and appending elements with JS

Example code
var jqxhr=$.getJSON("http://search.twitter.com/search.json?callback=?",{q:query},
function(data) {
... question.
});
Question
Now i need to create for each tweet result something like this (for example...)
<article class="tweet">
<header>
<img class ="tweet_img"src="data.profile_image_url"/>
</header>
<p class="tweet-text">data.text</p>
</article>
Well, i know several ways to append each result to the document:
Creating a big HTML string and add the data from JSONP and append this to some container.
Create a p element, a header element... work with them and after that append a final Element to some container.
Now the question is: with your experience what is the correct way to do this?
I mean the correct way using good principles.
Please dont ask about the html, it's dumb example.
Thanks.
Well, best practices will tell you not to use the innerHTML property of a DOM element, which is what you'd be doing with option 1. But unless you are concerned about immediately operating on the code with Javascript, attaching events, or security concerns around tag injection (I don't know how much this is an issue anymore) then creating an HTML string and inserting it using innerHTML is going to be a lot quicker and easier to update.
There are several valid approaches that each have their own advantages...
The technique of just generating the HTML as a string in your java code and adding it with .innerHTML is known to be one of the fastest performing approaches...but it provides very little validation of your HTML.
Alternatively, you can build the HTML using DOM methods directly, creating tags and appending them to build the structure. This is generally safer in that you have more validation going on, but the DOM methods are extremely wordy, which makes them a bit painful to type...and the code is even more verbose as you have to add attributes one at a time as well.
My personal preference, especially since you're already using JQuery, would be to build the tags and text nodes using JQuery, and put them together using JQuery, which allows you to do so in bite-sized, more human-verifiable units, without becoming overly verbose.
This also has the advantage that JQuery's methods of producing new tags give you additional support for older browsers that did not adhere to DOM standards. Hopefully you don't actually have to care whether your page works for those older browsers, but more compatibility never hurts either.
In that approach, you'd write something like the following:
var article = $('<article class="tweet"></article>');
var header = $('<header></header>');
var image = $('<img class="tweet_img" src="' + data.profile_image_url + '"></img>');
var tweet = $('<p class="tweet-text">' + data.text + '</p>');
header.append(image);
article.append(header, tweet);
$("#id_of_content_area_to_add_the_tweet_to").append(article);
The cleanest way I know how is to use a template system like Mustache, instead of "HTML in JS"
var template = html_string, //HTML from a string or from AJAX
data = {...data...}, //structured data
html = $(Mustache.render(template,data)); //wrap in jQuery to operate
html.appendTo(somewhere_in_DOM);
If you want to attach some event handlers to the elements then you should generate them separately.
But if you don't want to attach any event handler then i will recommend first method
$("body").append('<article class="tweet"><header><img class ="tweet_img" src="'+data.profile_image_url+'"/></header><p class="tweet-text">'+data.text+'</p></article>')
I will recommend you to use some Template engine like Handlebars.js Which is the right solution for your problem.
Which is having many more options which has many more conditional options which can be useful in feature. Just visit the above link you will have some idea.

Is it possible to make a change with jQuery and then immediately reverse that change?

I have a pretty specific scenario where I would like to select all elements with jQuery, make a CSS change, save the elements, then reverse the change I made.
The Goal
I created a jQuery plugin called jQuery.sendFeedback. This plugin allows the user to highlight areas of the screen, as shown in this demo. When they submit their feedback the plugin grabs all the HTML on the page and dumps it into a callback function. Like so:
$('*').each(function ()
{
$(this).width($(this).width());
$(this).height($(this).height());
});
var feedbackInformation = {
subject: $feedbackSubject.val(),
details: $feedbackDetails.val(),
html: '<html>' + $('html').html() + '</html>'
};
if (settings.feedbackSent)
settings.feedbackSent(feedbackInformation);
The callback function accepts this feedback information and makes an AJAX call to store the page HTML on the server (this HTML includes the red box highlights the user drew on the screen). When someone from tech support needs to view the user's "screen shot" they navigate to a page that serves up the stored HTML so the developer can see where the user drew their highlights on the screen.
My original problem was that different screen resolutions made the elements different sizes and the red highlights would highlight the wrong areas as the screen changed. This was fixed pretty easily by selecting all elements on the page and manually setting their height and width to their current height and width when the user takes the snap shot. This makes all the element sizes static, which is perfect.
$('*').each(function ()
{
$(this).width($(this).width());
$(this).height($(this).height());
});
The Problem
The issue with this is that when the plugin is done transmitting this HTML the page currently being viewed now has static heights and widths on every element. This prevents dropdown menus and some other things from operating as they should. I cannot think of an easy way to reverse the change I made to the DOM without refreshing the page (which may very well end up being my only option). I'd prefer not to refresh the page.
Attempted Solution
What I need is a way to manipulate the HTML that I'm sending to the server, but not the DOM. I tried to change the above code to pull out the HTML first, then do the operation on the string containing the HTML (thus not affecting the DOM), but I'm not quite sure what I'm doing here.
var html = '<html>' + $('html').html() + '</html>';
$('*', html).each(function ()
{
$(this).width($(this).width());
$(this).height($(this).height());
});
This did not work. So either I need to be able to manipulate the string of HTML or I need to be able to manipulate the DOM and undo the manipulation afterward. I'm not quite sure what to do here.
Update
I employed the solution that I posted below it is working beautifully now. Now I am wondering if there is a way to statically write all the css for each element to the element, eliminating the need for style sheets to be referenced.
I think you are mostly on the right track by trying to make the modifications to the HTML as a string rather than on the current page for the user.
If you check this post, you might also want to follow the recommendation of creating a temporary <div> on the page, cloning your intended content to the new <div> ensuring it is invisible using "display:none." By also putting a custom Id on the new <div> you can safely apply your static sizing CSS to those elements using more careful selectors. Once you have sent the content to the server, you can blow away the new <div> completely.
Maybe?
After much pain and suffering I figured a crude but effective method for reverting my modifications to the DOM. Though I hadn't gotten around to trying #fdfrye's suggestion of cloning, I will be trying that next to see if there is a mroe elegant solution. In the meantime, here is the new code in case anyone else can benefit from it:
$('*').each(function () {
if ($(this).attr('style'))
$(this).data('oldStyle', $(this).attr('style'));
else
$(this).data('oldStyle', 'none');
$(this).width($(this).width());
$(this).height($(this).height());
});
var html = '<html>' + $('html').html() + '</html>';
$('*').each(function () {
if ($(this).data('oldStyle') != 'none')
$(this).attr('style', $(this).data('oldStyle'));
else
$(this).removeAttr('style');
});
When I'm looping through every element and modifying the css, I log the original value onto the element as data. After I assign the DOM HTML to a variable I then loop through all elements again and restore the style attribute to its original value. If there was no style attribute then I log 'none' to the element data and then remove the style attribute entirely when looping through again.
This is more performance heavy than I wish it was since it loops through all elements twice; it takes a few seconds to finish. Not horrible but it seems like a little much for such a small task. Anyway, it works. I get a string with fixed-sized HTML elements and the DOM goes back to normal as if the plugin never touched it.

Template strings with JavaScript

I'm writing an application with some client-side JS that I use to update the DOM reasonably often.
The thing is that doing something like:
$('#id').html('<div class="' + class + '">' + content + '</div>');
multiple times and leaving HTML lying randomly round your JavaScript isn't very pretty and difficult to maintain.
Is there a JavaScript equivalent (or alternate solution) to the way Lithium handles this in it's view helpers.
See:
http://li3.me/docs/lithium/template/Helper::_render()
http://li3.me/docs/lithium/util/String::insert()
For examples.
Basically in the PHP version you would make an associate array of common strings and a simple method to replace to replace certain marked parts of those strings with variables of the same name.
Another silly example (psuedo-code:)
var strings = {
'div': '<div {:attr}>{:content}</div>'
};
console.log(render('div', {id: 'newElem'}, 'Hello, World!'));
// Output: <div id="newElem">Hello, World!</div>
If you have any better suggestions on how you handle storing HTML in your JavaScript and keep it from going all over the place then I'd very much like to hear it.
Yes, use jQuery templates or Underscore templates (my favorite) or any other JS templating engine out there.
Also, check this question for a discussion on performance of templating engines: Improve jQuery template performance
If you don't want to use a templating system, and have many html snippets that must be created many times, then you can use another technique :
Create a div in your main html file, give it a specific css class or id
Using css, make this div invisible
Inside is div, create the "template" divs, each one will contain a "snippet", each one with proper class or id
Using js (jquery, whatever) when you need it, clone the "template" div, append it where you need it, and then customize it.
Using jquery this is very easy, and your template "divs" are asy accessible to any html designer.
I'm on a mobile device now, and posting code snippets is a bit difficult, but let me know if want some examples.
jQuery encourages you to dynamically construct your DOM nodes instead of doing string concatenation:
$("#id").html(
$("<div />").addClass(class).text(content))
In general, you can use the jQuery attribute methods to construct such nodes, and many of these methods take mappings as you say you like. For example:
$("#id").append(
$("<div />")
.attr({id: "newElem"})
.css({width: "100%", color: "red"}))

Is there a way to update a jgrowl message?

Is there a way using jgrowl(or any other growl like popup) to update a message if it hasn't been removed yet and if it has been removed just simply add it like normal?
I think I found some changes that will make it possible to do what I want with jgrowl. http://www.moonlitscript.com/post.cfm/jgrowl-modification
Instead of modifying the plugin, I think an easier way would be to make a function to find text inside the growl, then remove it. I've tested the code below, it works and it doesn't appear to cause any problems with the plugin.
This function will remove the growl based on it's contents
function clearGrowl(txt){
$('.jGrowl-notification:contains("' + txt + '")').remove();
}
If you only want to target the header of the growl, then modify it as follows:
function clearGrowl(txt){
$('.jGrowl-notification .header:contains("' + txt + '")').remove();
}

Categories