ASP.Net MVC: Upload Image to Database and Show Image “Dynamically” Using a View
- Sean
Oddly enough this came about from me wanting to do this, figuring it out, and then deciding not to bother with it. So there's a possibility this will happen to you too. Well that's not completely true. The first half where I was uploading and showing from a database, but showing an image through a view to mimic the .ashx functionality of WebForms is still pretty useful.
Saving the Image
First off, here's the look of the table:
So pretty simple table. Most important parts are the ImageData and ContentType. Why? Well let's look at the action needed to save the image:
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Upload(PhotoForSingleItem photo) { //PhotoForSingleItem is just a class that has properties // Name and Alternate text. I use strongly typed Views and Actions // because I'm not a fan of using string to get the posted data from the // FormCollection. That just seems ugly and unreliable to me. //PhotoViewImage is just a Entityframework class that has // String Name, String AlternateText, Byte[] ActualImage, // and String ContentType PhotoViewImage newImage = new PhotoViewImage(); HttpPostedFileBase file = Request.Files["OriginalLocation"]; newImage.Name = photo.Name; newImage.Alt = photo.AlternateText; //Here's where the ContentType column comes in handy. By saving // this to the database, it makes it infinitely easier to get it back // later when trying to show the image. newImage.ContentType = file.ContentType; Int32 length = file.ContentLength; //This may seem odd, but the fun part is that if // I didn't have a temp image to read into, I would // get memory issues for some reason. Something to do // with reading straight into the object's ActualImage property. byte[] tempImage = new byte[length]; file.InputStream.Read(tempImage, 0, length); newImage.ActualImage = tempImage ; newImage.Save(); //This part is completely optional. You could redirect on success // or handle errors ect. Just wanted to keep this simple for the example. return View(); }
And here's the mark up to get this ball a rollin':
<form method="post" enctype="multipart/form-data" action="Photo/Upload"> <div> <span> Name: </span> <span> <input type="text" id="Name" name="Name" /> </span> </div> <div> <span> Alternate Text: </span> <span> <input type="text" id="AlternateText" name="AlternateText" /> </span> </div> <div> <span> Image </span> <span> <input type="file" id="OriginalLocation" name="OriginalLocation" /> </span> </div> <div> <input type="submit" value="Upload" /> </div> </form>
Biggest thing to notice in the markup is the enctype="multipart/form-data". This is a must to upload images. It was something I was missing originally and annoyed the hell out of me.
Showing the Image
So now that we have a we to upload the image, how the hell do you use it? Well that's not too hard. It just involves a new type of result, an action, and an img element.
So the first thing you need is an image result, and in using my superior intellect I came up with such a thing. And by superior intellect I mean I used StackOverflow. Oddly enough though, it's actually the second post that I got it from and I changed it a little. However, it was very useful.
using System.Web; using System.Web.Mvc; using System.IO; public class ImageResult : ActionResult { public String ContentType { get; set; } public byte[] ImageBytes { get; set; } public String SourceFilename { get; set; } //This is used for times where you have a physical location public ImageResult(String sourceFilename, String contentType) { SourceFilename = sourceFilename; ContentType = contentType; } //This is used for when you have the actual image in byte form // which is more important for this post. public ImageResult(byte[] sourceStream, String contentType) { ImageBytes = sourceStream; ContentType = contentType; } public override void ExecuteResult(ControllerContext context) { var response = context.HttpContext.Response; response.Clear(); response.Cache.SetCacheability(HttpCacheability.NoCache); response.ContentType = ContentType; //Check to see if this is done from bytes or physical location // If you're really paranoid you could set a true/false flag in // the constructor. if (ImageBytes != null) { var stream = new MemoryStream(ImageBytes); stream.WriteTo(response.OutputStream); stream.Dispose(); } else { response.TransmitFile(SourceFilename); } } }
And here's how you use the actual result.
[AcceptVerbs(HttpVerbs.Get)] public ActionResult ShowPhoto(Int32 id) { //This is my method for getting the image information // including the image byte array from the image column in // a database. PhotoViewImage image = PhotoViewImage.GetById(id); //As you can see the use is stupid simple. Just get the image bytes and the // saved content type. See this is where the contentType comes in real handy. ImageResult result = new ImageResult(image.ActualImage, image.ContentType); return result; }
And the markup would go a little sumthin' like dis:
<img src="/Photo/ShowPhoto/1" alt="" />
And now you too can upload an image to a database, show it, and then decide just to physically host the images anyway. Next post will be about how to use this with jQuery and asynchronously. I bet you can't wait!
ASP.NET MVC: Attributes and Semi Dynamic Check for Request Parameters
- Sean
If I were self involved I would say something silly like IN THIS AWESOME POST but I'm not. However in this post that is awesome I gave some examples of how to use attributes to set defaults or just check to see if an incoming id could be matched to a record somewhere... Sorry lost my track of thought. Watching the begining of Transformers... You know the part where it could have been good.
Anyway, I figured I'd add another debatable use for an attribute, the CheckForGivenRequest one. Basically in the other post I had something that was specific it was checking for, this is used if you are checking for a request parameter but you don't want to make an attribute for each and every one of them.
//This is so you can use it many times on the same method //I know, I know... duh [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class CheckForGivenRequestAttribute : ActionFilterAttribute { //The constructor to set what should be looked for //Default amount is what it should be set if not there public CheckForGivenRequestAttribute(String requestParameterName, Object defaultAmount) { DefaultAmount = defaultAmount; RequestParameterName = requestParameterName; } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); //Check the extra request parameters (ie &someId=1) for if it exists //If it doesn't exist, then add it if (!filterContext.ActionParameters.ContainsKey(RequestParameterName)) { filterContext.ActionParameters.Add(RequestParameterName, null); } //If it's null set to the default filterContext.ActionParameters[RequestParameterName] = filterContext.ActionParameters[RequestParameterName] ?? DefaultAmount; } //Just the properties, nothing to see here. Go away... private Object DefaultAmount { get; set; } private String RequestParameterName { get; set; } }
And then the use of it:
[CheckForGivenRequestAttribute("someStupidId", 1)] public ActionResult INeedAnExample(Int32 someStupidId) { ... }
Now you might ask why not make that attiribute generic to avoid boxing.
Why not make that attribute generic to avoid boxing?
Glad you asked. Turns out that you can't make attributes generic. Aparrently it's somwhat debatable why but not possible at the time being. Besides, the ActionParameters collection is <String, Object> anyhow, so at some point any stuct would be boxed anyhow.
On a side note, I never noticed this before, but when one of the non descript Autobots crashes near a pool, some kid is there to ask if he is the Tooth Fairy? Seriously? Are kids really that dumb? Cause every picture I've seen of the Tooth Fairy has been a 20 foot tall metal thing with no discernible features.
jQuery Slide and Pairing a Div’s Hover With a Div’s Slide
- Sean
So this could be filed under "I did it because I can" (which is a really good mantra in science...) and am not sure I'll use it but it is useful in the concept.
The idea is to set the hover on one div to show/hide another div WITHOUT having to use some kind of id/name trick (as in setting the id to "divHide1") and to have it be completely self setting in the .ready method. Why would this be at all useful? Say you want to roll through a list of things and generate the divs, but want to defer the jQuery until the page has loaded. And you don't want to have to resort to:
<div id="someDiv<%= someId %>"></div>
like structure where you parse some identifier from the id property. Mostly because you have no idea how many someDiv1s there could be on the page. It could be a highly reused name (someDiv) and that could lead to a mess.
Also the reason 'cuz. If you have any more questions of why after that answer, well you're just being annoying.
Anywho, here's the actual html for structure.
<div class="mainDiv"> <div class="showDiv" id="oneTop"> Kaneda? </div> <div class="slideDiv" id="one"> What do you see?! </div> </div> <div class="clear"></div>
Now I get that this isn't off a foreach, but it doesn't take much to figure out that it's easily made into a loop if you just loop it over and over. Why? Because first there are no ids or names so that you can't have two of the same name and also because that chunk is self contained.
So what is going on there? Simple, you have a parent container that holds a div to hold and a div to hover over and the other that will be shown/hidden.
Here's the styles involved just incase you care:
<style type="text/css"> .clear { clear:both; } .leftDiv { float:left; } .mainDiv { margin:5px; height:200px; } .rightDiv { float:left; } .showDiv { float:left; margin-right:5px; height:200px; background-color:Silver; } .slideDiv { background-color:Teal; height:200px; position:absolute; float:left; z-index:100; } </style>
That doesn't entirely matter, but it does to show the div "sliding" over another div. Kind of a little somethin' somethin' I threw in at no extra cost. Now for the jQuery:
First the method for setting the mouse over and out events for the show div, we turn to .hover:
function setHover(currentSlideDiv, currentStableDiv) { currentStableDiv.hover ( //first parameter is the method for showing the div on mouse over function() { if (currentSlideDiv.is(":hidden")) { currentSlideDiv.show("slide", { direction: "left" }, 100); } }, //second parameter is the method for hiding the div on mouse out function() { if (currentSlideDiv.is(":visible")) { currentSlideDiv.hide("slide", { direction: "left" }, 100); } } ); };
Now for the method that actually uses these:
//This is used to take in one of the main divs and set all the //show and slide divs within. function setChildrenDivs(mainDiv) { //get all the show and slide dvis within the main div var mainChildrenStableDiv = jQuery(mainDiv).children(".showDiv"); var mainChildrenSlide = jQuery(mainDiv).children(".slideDiv"); //loop through the list of show divs for (var loopCounter = 0; loopCounter < mainChildrenStableDiv.length; loopCounter++) { //Get the show div and it's corresponding slide div using the //two lists and the current counter. var currentStableDiv = jQuery(mainChildrenStableDiv[loopCounter]); var currentSlideDiv = jQuery(mainChildrenSlide[loopCounter]); //This is to make sure the slide is where it should be. //to the right of the show div. var containerPosition = jQuery(currentStableDiv).position(); jQuery(currentSlideDiv).css({ left: containerPosition.left + currentStableDiv.width() }); //Set the mouse over and the mouse out on the show div setHover(currentSlideDiv, currentStableDiv); } }
Now the final touch, the .ready method:
jQuery(document).ready
(
function()
{
//hide all the slide divs
jQuery(".slideDiv").hide();
//find all the parent divs
var mainDivs = jQuery(".mainDiv");
//set the children
for (var loopCounter = 0; loopCounter < mainDivs.length; loopCounter++)
{
setChildrenDivs(mainDivs[loopCounter]);
}
}
);
And boom, now you have multiple show/hide (Slide in this instance). Now if I could just find a use for it...
Oh yeah and you'll need jQuery 1.3.2 and jQuery ui 1.7.2 to use this. At least those are the versions I know this works with.
Update: Due to popular demand... one person... Source can be found here.
Entity Framework: LINQ to Entities only supports casting Entity Data Model primitive types
- Sean
So in typical tool fashion I posted this little gem without realizing a glaring error... the order by clause. The whole idea is to create a method that can get a collection, sort it, then grab a certain number for paging. The issue was this:
Expression<Func<K, IComparable>> orderBy
The problem comes in when the entire expression is run, meaning when Entity Frame work takes:
context.Select(selectMethod).Where(whereClause).OrderBy(orderBy).ToList();
and creates SQL out of it. Entity Framework doesn't really know how to handle IComparable as it has not primitive/sql type to match to it. Why it can't see the underlying type of say DateTime, no idea, but this is life.
So this should be an easy fix, right? Eh... yeah. First I thought instead of IComparable I could just convert to some kind of primitive type so that the EF could be on it's merry. Not so much. Turns out this probably isn't possible.
Well thanks to a push from Ben M at The O Flow I got to thinking about how to attack this. Instead of sending in an expression for the order by, why not send in a method that would take in a query and tell it how to order itself. Sounds hard right? (If not then you're obviously too cool for this school) Well it's not, just a different way to think about it.
Right off the bat, the change to the method signature would look like this:
Old order by parameter signature:
Expression<Func<K, IComparable>> orderBy
New order by parameter signature:
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy
So what does that mean? It means that I am going to supply the method with a method that will take in a query and return an ordered query... well not ordered yet per se but the blueprint on how to order when that time comes around. Now here's how that's used:
First you need the query:
var initialQuery = query
.Where
(
somethingEqualsSomething
);
Then you apply the orderby method, and in the case of the original paging method, the paging too:
var orderedQuery = orderBy(initialQuery);
returnValue = orderedQuery
.Skip(numberToShow * realPage)
.Take(numberToShow)
.ToList();
So overall, doesn't look too much different. Just instead of supplying the OrderBy method with a Func, you give a method that creates an ordered query.
How would you use this? Remember the signature was (whereClause, selectClause, orderBy, pageNumber, numberToShow, realPage, totalCountOfPages)
Tools.GetListForGrid
(
tool => tool.EntityId == userId,
tool => new { Name = tool.Name }, //Select Clause, I'll get to that next
toolOuter => toolOuter.OrderBy(toolInner => toolInner .Name), //OrderBy
...
)
Two things you might notice. One would be the OrderBy signature:
toolOuter => toolOuter.OrderBy(toolInner => toolInner .Name),
What the hell? Remember the method you are sending takes in a query and returns an ordered query. toolOuter is your query, toolOuter.OrderBy(toolInner => toolInner .Name) is your blueprint (IOrderedQueryable) on how it should be queried.
Second thing is that when I showed how to use the OrderBy method above:
var orderedQuery = orderBy(initialQuery);
returnValue = orderedQuery
.Skip(numberToShow * realPage)
.Take(numberToShow)
.ToList();
I didn't include the select clause. Partially because I didn't want to complicate it yet, partially because it has it's own issue. If you're like me, you use the select clause a lot. Why? Because it limits the amount of information you take from the database. Say if you are trying to fill a drop down list, why select an entire Tool object (Which could have a ton of mapped properties) when all you need is Id and Name? Seems silly. That's where the Select clause comes in. Now the issue is where to put the order by. You would think after the select clause, since you want to sort on only what you are selecting. Problem is, with paging that gets screwed up. The way sql server "pages" is that it selects twice.
Select all the things that match this where clause.
Select a certain number of items from that starting at X.
The first select creates a dataset with row numbers, and the second one selects the items based on those row numbers. IE take 5 starting at row 5. Now the way the EF handles the order by is to grab the info you need from the Select (Ordered by something... you don't get to choose) and THEN order and grab. As you can guess this may not give the needed results. So how can this be solved? Order before the select as witnessed in the new and improved method:
public static IList<K> GetListForGrid<T, K> ( this ObjectQuery<T> query, Expression<Func<T, Boolean>> somethingEqualsSomething, Expression<Func<T, K>> selectClause, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, Int32 pageNumber, Int32 numberToShow, out Int32 realPage, out Int32 totalCountOfPages ) { IList<K> returnValue; Int32 totalItemCount = query .Count ( somethingEqualsSomething ); totalCountOfPages = Methods.TotalCountOfPages(totalItemCount, numberToShow); realPage = Methods.GetRealPage(totalCountOfPages, pageNumber); var initialQuery = query .Where ( somethingEqualsSomething ); var orderedQuery = orderBy(initialQuery); returnValue = orderedQuery .Select ( selectClause ) .Skip(numberToShow * realPage) .Take(numberToShow) .ToList(); return returnValue; }
And usage:
Tools.GetListForGrid
(
tool => tool.Id == userId,
tool => new { Name = tool.Name }, //Select Clause
toolOuter => toolOuter.OrderBy(toolInner => toolInner .Name), //OrderBy
pageNumber,
amountToShow,
out realPage,
out totalCountOfPages
);
Now this one actually works with Entity Framework and not just Linq to objects like the last one.
ASP.Net MVC, jQuery, JSon, and paging… how’s that for a SOE title?
- Sean
One of the things I've come to realize is how easy it easy to do a lot of things with these three buzzwords. In fact, I'm pretty convinced it's so easy that it's actually complex and I am a genius. Not buying it? Neither am I.
So for an experiement the other day I decided to try my hand at some sort of dynamic grid using jQuery's ajax fun and JSon. Just so happens that this works really well with MVC's REST like makeup. Don't know what REST is? On a very tool level, it's using a url and a command to tell the server what to do. So something like:
www.byatool.com/users/dostuff
Could mean either get all users (if using get) or create a user (If using post). And yes that is probably a ridiculously simplistic view so I'd suggest consulting the Wikitruth. In an MVC sense this would be:
Controller: Users
Action: dostuff
Now most likely your Get All Users action isn't going to the same as your Add A User action, but it was just a stupid example ok?
However, with jQuery what this means is you have a simple url that it can call and get information from, making it incredibly easy to set up a dynamic grid.
So first off, lets say I have a Forum controller with an action of IndexJquery... yeah I know cheesy name, but it gets the job done. Basically the method IndexJquery would have to take in a page number and optionally (And for this example) how many items to show along with a sort. With that it should return a JSon "object" that will be in this example holds first page, last page, next page, previous page, sortBy, and some kind of list of stuff. (For the two people actually reading this, comment if you want the c# code. It's really just basic MVC stuff.)
The markup for this is pretty simple. I have a div to hold the grid, four directional divs that work as buttons (First, Previous, Next, Last), and two div "butons" for how many items to show.
<div>
<div id="divHolder">
</div>
</div>
<div>
<div id="divFirstPage" class="divLink floatLeft">
<<
</div>
<div id="divPreviousPage" class="divLink floatLeft">
<
</div>
<div id="divNextPage" class="divLink floatLeft">
>
</div>
<div id="divLastPage" class="divLink floatLeft">
>>
</div>
</div>
<div class="clear"></div>
<div>
<div id="divAmountToShowOne" class="divLink floatLeft">
1
</div>
<div id="divAmountToShowFive" class="divLink floatLeft">
5
</div>
</div>
<div class="clear"></div>
As you can see exactly as advertised.
Ready for the call? It's waaaaay hard:
function getGrid(pageNumberIn, amountToShowIn, sortByIn) { jQuery.getJSON ( //This is the url for the information I need "http://www.someurl.com/Forum/IndexJquery/", //this is the construction of the "object" to send... really this just //means that I have a method somewhere looking for pageNumber, //amountToShow, and sortBy { pageNumber: pageNumberIn, amountToShow: amountToShowIn, sortBy: sortByIn }, //This is the method to call once this ajax transaction has completed... //transaction may not be the best word. Basically it has to be a method //that takes in the result from the getJSon call returned ); }
Next would be the script to actually create the grid. Looks verbose, but most likely thats because I didn't refactor much.
function returned(jsonObject) { //Have to remove all the previous click event handlers since because //this is all client side, there's no "refresh" and therefore the object //is still in memory. So even though I might call the method to get the //information, the "objects" are still in memory. jQuery("#divFirstPage").unbind("click"); jQuery("#divLastPage").unbind("click"); jQuery("#divNextPage").unbind("click"); jQuery("#divPreviousPage").unbind("click"); jQuery("#divAmountToShowOne").unbind("click"); jQuery("#divAmountToShowFive").unbind("click"); //Ok so now that the event is not being listened to, set up the listeners //The idea is to call that getGrid method and pass in the values straight //off the previously returned json object. Using jQuery's easy .click method //makes this so simple. jQuery("#divFirstPage").click(function() { getGrid(jsonObject.FirstPage, jsonObject.AmountToShow, jsonObject.SortBy); }) jQuery("#divPreviousPage").click(function() { getGrid(jsonObject.PreviousPage, jsonObject.AmountToShow, jsonObject.SortBy); }) jQuery("#divNextPage").click(function() { getGrid(jsonObject.NextPage, jsonObject.AmountToShow, jsonObject.SortBy); }) jQuery("#divLastPage").click(function() { getGrid(jsonObject.LastPage, jsonObject.AmountToShow, jsonObject.SortBy); }) jQuery("#divAmountToShowOne").click(function() { getGrid(0, 1, jsonObject.SortBy); }) jQuery("#divAmountToShowFive").click(function() { getGrid(0, 5, jsonObject.SortBy); }) //Again since this is client side, the divHolder "object" still is holding //the previous results. These have to be cleared. jQuery("#divHolder").children().remove(); //Create the table and loop through the list. var mytable = document.createElement("table"); var mytablebody = document.createElement("tbody"); for (loopCounter = 0; loopCounter < jsonObject.ListForViewing.length; loopCounter++) { var currentItem = something.ListForViewing[loopCounter]; var mycurrent_row = document.createElement("tr"); var mycurrent_cell = document.createElement("td"); var currentText = document.createTextNode(currentItem.ForumName); mycurrent_cell.appendChild(currentText); mycurrent_row.appendChild(mycurrent_cell); mytablebody.appendChild(mycurrent_row); } mytable.appendChild(mytablebody); //Don't forget to add the table to the div!!11 jQuery("#divHolder").append(mytable); return false; }
And boom. So easy even a ca... tool can do it. Now if you want this grid to come preloaded, it's pretty easy:
jQuery(document).ready
(
function setPage()
{
getGrid(0, 10, null);
}
)
jQuery: Add a Pop Up Div to a Link Dynamically
- Sean
So as an exercise to learn more about jQuery, I decided to redo this little gem using jQuery. Have to say though only technically XHTML compliant, it worked out much better and with less code. So the idea is to have something really easy for non programmers (You know, lesser people) to be able to have a pop up comment added to some chunk of text on a web site. What I came up with before was ok but kind of annoying since it looked like this:
<a onclick="return ShowCommentForPost('1', this, 'THIS IS SO STUPID!');" href="www.byatool.com">word.</a>
Kind of annoying since I would have to explain that '1' is the name and it has to be unique for every one of these, this isn't really understood by those people, and well it just seems more complicated then it needs to be. So what if I told you it could look like this?
<a href="http://www.byatool.com" class="showItLink" xmlns:comment="hihihi">HI</a>
Gorsh, that seems even better and it works in both IE and Firefox... which might be a first for this site and anything javascript. First let's get the css out of the way, shall we?
.showItLink { } .postComment { background-color:Gray; border-color:Black; border-style:dotted; border-width:thin; color:White; margin-right:3px; padding:3px; position:absolute; text-decoration:none; z-index:50; }
Most of the is just for looks, but like the older example I've called upon the power of position:absolute and z-index well that's just pulling it in front of everything else.
Next part will be the actual code, and if you take the method from THE POSITION ABSOLUTE POST
function SetTopAndLeft(parentContainer, elementToSet) { var containerPosition; containerPosition = $(parentContainer).position(); $(elementToSet).css({ top: containerPosition.top + 10, left: containerPosition.left + 10 }); }
and add in a simple span creation method:
function CreateDiv(innerText, cssClass) { var spanToAdd; spanToAdd = document.createElement('span'); spanToAdd.className = cssClass; spanToAdd.innerHTML = innerText; return spanToAdd; }
you're ready for the actual fun part... making sure something pops up when the link is clicked.
jQuery(document).ready //Everything inside this will load as soon as the DOM is loaded and before the page contents are loaded.* ( function() //this is the start of an anonymous method { jQuery(".showItLink").click //find all things with the .showItLink class and assign the click event to the next anonymous method ( function(event) //this is the start of an anonymous method for the click event { var containerPosition; var createdSpan; var comment; comment = jQuery(this).attr("xmlns:comment"); //Get the value from the comment attribute on the link. createdSpan = jQuery(this).children(".postComment"); //Find a possible span already attached to the link if it exists. The span is of class 'postComment' if (createdSpan.length == 0) //span doesn't exist { createdSpan= CreateDiv(comment, "postComment"); //create the span jQuery(this).append(createdSpan); //Add the span to the link jQuery(this).children(".postComment").hide(); //Make sure the new span is hidden } SetTopAndLeft(this, createdSpan); //Set the position of the span jQuery(createdDiv).toggle(); //This will hide if it's showing, show if it's hidden... kind of nice huh? event.preventDefault(); //Equivalent to false. Need this for Firefox. } ); } );
And boom you have something that works. Hooray.
Couple things of note:
.Hide - At first I though this would screw up my class for the span by removing the current class and adding display:none. Turns out it doesn't harm the original class. Kind of nice.
.Toggle() - This is really nice. It will hide if it is showing and show if hidden. Stupid easy to use and is pretty effective. Just like .Hide, the class of the element is not harmed.
$ versus jQuery - Some people might notice that I am not using the short hand $ for my jQuery calls. Turns out that it might be safer this way. There are other javascript libraries that use the $ short hand like prototype. I ran into this with WordPress since it uses both jQuery and Prototype and blocks jQuery from using $ since it could conflict with other libraries. Weeeee!
JQuery, Position : Absolute, and How to Make It All Work
- Sean
This is a really quick one but when I was taking my cheesy pop up and reworking it using JQuery (After Andre the Annoying wouldn't shut up about it), I ran into a fun problem: position:absolute wasn't working like it should. You know "absolute is positioned at the specified coordinates relative to its containing block.". Meaning it should at worst show up within it's container, where ever that is. Now IE is fine with that and the thing was showing up well:
Firefox? Not so much:
Well... turns out JQuery pretty much does it for me. With a simple method, you can set one element's position relative to a parent's position:
function SetTopAndLeft(parentContainer, elementToSet) { var containerPosition; containerPosition = $(parentContainer).position(); $(elementToSet).css({ top: containerPosition.top + 10, left: containerPosition.left + 10 }); }
Really simple, you get the position of the parent container and you set the child element's top and left to it. Or in this case, I have it just off since hiding the parent container could be problematic. (Say if the parent container is a link AND NOW YOU CAN'T FIND IT TO CLICK ON IT AND THINGS HAPPEN BAD THINGS AND THE WORLD EXPLODES BECAUSE OF YOU!)
And boom, you can for once be a winner just like me.
Add a Pop Up Div to a Link Dynamically
- Sean
Sometimes in life you have to ask "should I do this", this is not one of those times. The idea is simple, click on a link and a div appears over the link with some kind of message in it. Kind of like being able to add a pop up note to a word. If you are absolutely amazed by that, don't be afraid. Most likely you'll die soon from forgetting to breathe. However, if you are just slightly curious as to why and how, keep reading.
So why did I do this? Well it started with the idea of having something simple for a blog that has multiple authors: What if other authors could add notes to someone's post in the post. Well the idea of using some kind of text change (Like italics) sounded lame. I wanted something easy that could be replicated quickly and wouldn't be visible unless needed. Thus the onClick idea. Now the next problem I had was the class needed for the style sheet. As you can see, when the div is shown, it doesn't displace any of the items on the page. This is because I am using position:absolute and a high z-index. This allows for the div to lay on top of other things and not touch them. Problem with absolute is that it basicaly plants the div in relation to it's parent container. Now that whole parent container thing seems to be up for interpretation when you are talking about browsers. Each seems to deal with it the way it sees fit.
Originally I had it as a div that would contain this new div. This was a pain in the -ss. In IE it showed up over the div, FireFox not so much. So the next thought was to create a div to hold the div that held the div. Something that isn't exactly "user friendly" to be sure. Then it hit me, maybe I could put this in a link. After all, people who are viewing the blog would understand it's something they can click on (Provided I don't screw with the link styles too much) and it's easy for non coders to copy and paste.
So on to the promised land. First I'll just get the CSS out of the way since it's absolutely needed but needs little explanation:
.hidePostComment { visibility:hidden; position: absolute; z-index:-100; } .showPostComment { background-color:Gray; border-color:Black; border-style:dotted; border-width:thin; color:White; margin-right:3px; padding:3px; position:absolute; text-decoration:none; visibility:visible; width:100px; z-index:10; }
.hidePostComment
As you can see, I've screwed with the z-index, visibility, and position. Position I've already explained, and I think you can understand why visibility is hidden with this class. However, z-index might not be something you know about. Basically,z-index tells the browser where an item is in a vertical sense. When you look at a browser, there are actually a lot of layers regardless of the 2nd appearance. The z-index is used to bring something forward or backward. If I want the div to be behind say the text I am typing right now, it has to be at a z-index lower than the text. I used -100 in the example just to make sure it's behind anything. It's really an arbitrary number though. A positive number would make the div appear in front of the text (And in that case the text would not show up since it would be "behind" the div) which is what I did with the visibility class.
.showPostComment
Mostly just a bunch of visual changes like border and background color. However, you will also notice the the position is still absolute and that the z-index is now 10. (positive) The div will now effectively be "in front" of the link when it shows up giving it the pop up look. One Note: I had to add in text-decoration:none since the div is attached to the link and IE wants to drag the underline with the pop up causing the text to up with an underline. Kind of odd but no big deal.
Now for the code:
function BuildSelectableSpanForPost(spanName, parentElement, innerText) { var divToAdd; var parentContainer; //check to see if the parentElement is actually an element or string. If string, use it //to find the element. if (typeof parentElement == 'string') { parentContainer = document.getElementById(parentElement); } else { parentContainer = parentElement; } //Create the div //set the name (The name must unique since there could be a million "pop ups" per page //set the id //set the text for the div which is what we want to show up in the pop up divToAdd = document.createElement('span'); divToAdd.setAttribute('name', spanName); divToAdd.id = spanName; divToAdd.innerHTML = innerText; //Add the div to whatever element that was found. For this post it will be a link //but it doesn't really matter. parentContainer.appendChild(divToAdd); return divToAdd; }
So there is the building of the pop up div. Here's the method to be called by the onclick event:
function ShowCommentForPost(postName, parentElement, innerText) { var divName; var createdDiv; var parentContainer; //Same as before if (typeof parentElement == 'string') { parentContainer = document.getElementById(parentElement); } else { parentContainer = parentElement; } //See if the pop up div already exists. If it does, then don't create again //I didn't have this before and it would create a new div everytime //That's what some might call a surprise feature divName = 'comment' + postName; createdDiv = document.getElementById(divName); //Ooops, the div didn't exist, create it and add the hide class if (createdDiv == null) { createdDiv = BuildSelectableSpanForPost(divName, parentContainer, innerText); //this is a method found on this post ClassHandler.AddClass(createdDiv, 'hidePostComment'); } //this is a method based off this post //As you can guess it will show or hide depending on which class it already has. ShowHideElementBasedOnCss(createdDiv); return false; }
To start, there is the code to create the actual div.
Now for the actual use:
<a onclick="return ShowCommentForPost('1', this, 'THIS IS SO STUPID!');" href="www.byatool.com">word.</a>
Pretty easy to actually use right? The actual location doesn't really matter since it the method will always return false and therefore the link will never redirect. Also, you'll see that I put 1 as the name sent in. The name sent in doesn't matter what it is, but for every link it has to be different. If you are using this in a blog situation where there could be multiple blog posts in one page, I would suggest the name sent in would be the title and an increasing number.
If you got to this point and feel robbed of five minutes in your life, well just be happy this post robbed me of 15 minutes of mine.
Add, Remove, or Replace a CSS Class Using Javascript
- Sean
Now honestly, I think the all famous Prototype has something for this, but if you aren't using the all famous Prototype... well you're screwed. Until now.
This is the idea, you want show or hide something on a click of it.
<div onclick="HideOrShowMe();">
YAYAAYAYAY!
</div>
Annoying thing is trying to keep up with whether it's hidden or not. Now there are ways to do this for sure, but if you had them you wouldn't be here... or you just love idiotic banter. Either way, you're here.
To start, get a class going:
if (typeof ClassHandler != 'object') { ClassHandler = new Object(); }
Normal declaration. Yeehaw. Now we need a method to check for the class:
ClassHandler.CheckForClass = function(element, nameOfClass) { var returnValue = false; //Check to see if the element variable is a string or (hopefully) a control. //If it is a string, get the control that belongs to that id. if (typeof element == 'string') { element = document.getElementById(element); } //next you use a regular expression to check the className property for // the class you want. If it finds a match, you're good to go. if (element.className != '') { returnValue = new RegExp('\\b' + nameOfClass + '\\b').test(element.className); } return returnValue; }
Ok so now you have a method to check if it's there... but what about if you want to replace the old one with a new one? Well that could be broken into two methods first (Both that are useful). First one is the removal of the old class:
ClassHandler.RemoveClass = function(element, nameOfClass) { var returnValue = false; //You know the drill if (typeof element == 'string') { element = document.getElementById(element); } //Hey cool, I just used the CheckForClass method if (ClassHandler.CheckForClass(element, nameOfClass)) { //Real simple, replace the old //But you have to check if the nameOfClass exists with a preceding space //If it does, then you replace that with a space, other wise just replace //the nameOfClass with a space. element.className = element.className.replace((element.className.indexOf(' ' + nameOfClass) >= 0 ? ' ' + nameOfClass : nameOfClass),''); returnValue = true; } return returnValue; }
And now you'll have to add in the new one.
ClassHandler.AddClass = function(element, nameOfClass)
{
var returnValue = false;
//lalalala
if (typeof element == 'string')
{
element = document.getElementById(element);
}
//If className already has a value, precede the nameOfClass with a space
// otherwise just add it in.
if (!ClassHandler.CheckForClass(element, nameOfClass))
{
element.className += (element.className ? ' ' : '') + nameOfClass;
returnValue = true;
}
return returnValue;
}
Now for the replace:
ClassHandler.ReplaceClass = function(element, classToRemove, classToAdd)
{
var returnValue = false;
//african elephants only have four teeth for chewing their food.
if (typeof element == 'string')
{
element = document.getElementById(element);
}
//Remove the old one
//Add the new one
if(ClassHandler.CheckForClass(element, classToRemove))
{
ClassHandler.RemoveClass(element, classToRemove);
ClassHandler.AddClass(element, classToAdd);
returnValue = true;
}
return returnValue;
}
Now you might be thinking, "Tool, why don't you just use the replace method in the replace method." Well I suppose you could, but this way you have two other methods at your disposal for future use. Up to you really, part of this was because I actually needed those methods and the title says "Add, Remove, or Replace". I can't go against the title. I don't have that kind of strength.
Usage? Well there are couple ways you can do this.
if(ClassHandler.CheckForClass('someDiv', 'hide')) { ClassHandler.ReplaceClass('someDiv', 'hide', 'show'); } else { ClassHandler.ReplaceClass('someDiv', 'show', 'hide'); }
or this:
if(!ClassHandler.ReplaceClass('someDiv', 'hide', 'show')) { ClassHandler.ReplaceClass('someDiv', 'show', 'hide')); }
And other ways to be sure. Now I'm not saying this is the best way to do it, just a decent way... although looking back on this I wonder if I could clean up the regex stuff.


