Image Dropdownlist - asp.net

I am looking for a drop down list which can present the user with a series of images to choose from. Each image will be about 50x50 pixels and a small text description will be under the image. A jQuery and compatable ASP.NET solution would be preferred.

I wrote a super basic jQuery plug in to accomplish this. What will happen is a a fake drop down list will be created from an existing select tag. The original select will be hidden, and the fake menu will be shown. As the new menu is being created, it will callback to get the HTML to show for each option. In this function you can pass back an image.
(function($) {
$.fn.templatedSelect = function(options) {
var defaults = {
selectHandleImage : "selectHandle.gif",
width : "65px",
getOption : function(value, text) {
return text;
}
};
var opts = $.extend(defaults, options);
var $originalSelect = this;
var $container = $(document.createElement('div'))
.css("clear", "both")
.css("width", opts.width)
.hover(
function () {
$selectBox.css("border-color", "#000000");
},
function () {
if (!$menuItems.is(":visible"))
$selectBox.css("border-color", "#C0C0C0");
})
.attr('id', "imageSelect_container_" + this.attr('id'));
var $selectBox = $(document.createElement('div'))
.css("border", "solid 1px #C0C0C0")
.css("overflow", "hidden")
.css("width", "100%")
var $selectedItem = $(document.createElement('div'))
.css("padding", "4px");
var $selectHandle = $(document.createElement('div'))
.css("float", "right")
.css("background-color", "#F0F0F0")
.css("padding", "4px")
.css("cursor", "hand")
.click(function(e) {
ToggleMenuItems();
})
.html(
$(document.createElement('img')).attr("src", opts.selectHandleImage)
);
var $menuItems = $(document.createElement('div'))
.css("position", "absolute")
.css("margin-top", "-1px")
.css("border", "solid 1px #000000")
.css("background-color", "#FFFFFF")
.hide();
$originalSelect.children("option").each(function(i, selected) {
var $menuItem = $(document.createElement('div'))
.css("padding", "4px")
.html(opts.getOption($(this).val(), $(this).text()))
.val($(this).val())
.click(function(e) {
ToggleMenuItems();
$originalSelect.val($(this).val());
$selectedItem.html($(this).html());
})
.hover(
function () {
$(this).css("background-color", "#81BEF7");
},
function () {
$(this).css("background-color", "#FFFFFF");
})
.appendTo($menuItems);
});
//preset the selectedItem
$selectedItem.html(
$menuItems.children("div:eq("+$originalSelect[0].selectedIndex+")").html()
);
//put everything together
$selectBox.appendTo($container);
$selectHandle.appendTo($selectBox);
$selectedItem.appendTo($selectBox);
$menuItems.appendTo($container);
//hide the original select and put ours in
$originalSelect.hide();
$container.insertBefore($originalSelect);
$selectHandle.height($selectBox.height());
$menuItems.width($selectBox.width());
function ToggleMenuItems() {
if ($menuItems.is(":visible")) {
$menuItems.hide();
$selectBox.css("border", "solid 1px #C0C0C0");
} else {
$menuItems.show();
$selectBox.css("border", "solid 1px #000000");
}
}
}})(jQuery);
To use, call templatedSelect on your existing select. Also pass in a function to resolve the template for each item
$().ready(function() {
$('#selectId').templatedSelect({
getOption : function(v, t) {
return "<img src='" + v + "'/><br/>" + t;
}
});

Related

Scrolling only side menu div and others should be fixed when menu is open

I have a side menu and when it's open, the body can be partially seen. My side menu might be long so you could scroll on it. But when the menu is at the bottom you then scroll on the body, and I don't want this behaviour.
Similar to Scrolling only content div, others should be fixed but I'm using React. Other content should be scrollable when my side menu is closed. Think of the content as side menu in the example in the link. So far I'm using the same technique provided by that answer but it's ugly (kinda jQuery):
preventOverflow = (menuOpen) => { // this is called when side menu is toggled
const body = document.getElementsByTagName('body')[0]; // this should be fixed when side menu is open
if (menuOpen) {
body.className += ' overflow-hidden';
} else {
body.className = body.className.replace(' overflow-hidden', '');
}
}
// css
.overflow-hidden {
overflow-y: hidden;
}
What should I do with Reactjs?
You should make a meta component in react to change things on the body as well as changing things like document title and things like that. I made one a while ago to do that for me. I'll add it here.
Usage
render() {
return (
<div>
<DocumentMeta bodyClasses={[isMenuOpen ? 'no-scroll' : '']} />
... rest of your normal code
</div>
)
}
DocumentMeta.jsx
import React from 'react';
import _ from 'lodash';
import withSideEffect from 'react-side-effect';
var HEADER_ATTRIBUTE = "data-react-header";
var TAG_NAMES = {
META: "meta",
LINK: "link",
};
var TAG_PROPERTIES = {
NAME: "name",
CHARSET: "charset",
HTTPEQUIV: "http-equiv",
REL: "rel",
HREF: "href",
PROPERTY: "property",
CONTENT: "content"
};
var getInnermostProperty = (propsList, property) => {
return _.result(_.find(propsList.reverse(), property), property);
};
var getTitleFromPropsList = (propsList) => {
var innermostTitle = getInnermostProperty(propsList, "title");
var innermostTemplate = getInnermostProperty(propsList, "titleTemplate");
if (innermostTemplate && innermostTitle) {
return innermostTemplate.replace(/\%s/g, innermostTitle);
}
return innermostTitle || "";
};
var getBodyIdFromPropsList = (propsList) => {
var bodyId = getInnermostProperty(propsList, "bodyId");
return bodyId;
};
var getBodyClassesFromPropsList = (propsList) => {
return propsList
.filter(props => props.bodyClasses && Array.isArray(props.bodyClasses))
.map(props => props.bodyClasses)
.reduce((classes, list) => classes.concat(list), []);
};
var getTagsFromPropsList = (tagName, uniqueTagIds, propsList) => {
// Calculate list of tags, giving priority innermost component (end of the propslist)
var approvedSeenTags = {};
var validTags = _.keys(TAG_PROPERTIES).map(key => TAG_PROPERTIES[key]);
var tagList = propsList
.filter(props => props[tagName] !== undefined)
.map(props => props[tagName])
.reverse()
.reduce((approvedTags, instanceTags) => {
var instanceSeenTags = {};
instanceTags.filter(tag => {
for(var attributeKey in tag) {
var value = tag[attributeKey].toLowerCase();
var attributeKey = attributeKey.toLowerCase();
if (validTags.indexOf(attributeKey) == -1) {
return false;
}
if (!approvedSeenTags[attributeKey]) {
approvedSeenTags[attributeKey] = [];
}
if (!instanceSeenTags[attributeKey]) {
instanceSeenTags[attributeKey] = [];
}
if (!_.has(approvedSeenTags[attributeKey], value)) {
instanceSeenTags[attributeKey].push(value);
return true;
}
return false;
}
})
.reverse()
.forEach(tag => approvedTags.push(tag));
// Update seen tags with tags from this instance
_.keys(instanceSeenTags).forEach((attr) => {
approvedSeenTags[attr] = _.union(approvedSeenTags[attr], instanceSeenTags[attr])
});
instanceSeenTags = {};
return approvedTags;
}, []);
return tagList;
};
var updateTitle = title => {
document.title = title || document.title;
};
var updateBodyId = (id) => {
document.body.setAttribute("id", id);
};
var updateBodyClasses = classes => {
document.body.className = "";
classes.forEach(cl => {
if(!cl || cl == "") return;
document.body.classList.add(cl);
});
};
var updateTags = (type, tags) => {
var headElement = document.head || document.querySelector("head");
var existingTags = headElement.querySelectorAll(`${type}[${HEADER_ATTRIBUTE}]`);
existingTags = Array.prototype.slice.call(existingTags);
// Remove any duplicate tags
existingTags.forEach(tag => tag.parentNode.removeChild(tag));
if (tags && tags.length) {
tags.forEach(tag => {
var newElement = document.createElement(type);
for (var attribute in tag) {
if (tag.hasOwnProperty(attribute)) {
newElement.setAttribute(attribute, tag[attribute]);
}
}
newElement.setAttribute(HEADER_ATTRIBUTE, "true");
headElement.insertBefore(newElement, headElement.firstChild);
});
}
};
var generateTagsAsString = (type, tags) => {
var html = tags.map(tag => {
var attributeHtml = Object.keys(tag)
.map((attribute) => {
const encodedValue = HTMLEntities.encode(tag[attribute], {
useNamedReferences: true
});
return `${attribute}="${encodedValue}"`;
})
.join(" ");
return `<${type} ${attributeHtml} ${HEADER_ATTRIBUTE}="true" />`;
});
return html.join("\n");
};
var reducePropsToState = (propsList) => ({
title: getTitleFromPropsList(propsList),
metaTags: getTagsFromPropsList(TAG_NAMES.META, [TAG_PROPERTIES.NAME, TAG_PROPERTIES.CHARSET, TAG_PROPERTIES.HTTPEQUIV, TAG_PROPERTIES.CONTENT], propsList),
linkTags: getTagsFromPropsList(TAG_NAMES.LINK, [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF], propsList),
bodyId: getBodyIdFromPropsList(propsList),
bodyClasses: getBodyClassesFromPropsList(propsList),
});
var handleClientStateChange = ({title, metaTags, linkTags, bodyId, bodyClasses}) => {
updateTitle(title);
updateTags(TAG_NAMES.LINK, linkTags);
updateTags(TAG_NAMES.META, metaTags);
updateBodyId(bodyId);
updateBodyClasses(bodyClasses)
};
var mapStateOnServer = ({title, metaTags, linkTags}) => ({
title: HTMLEntities.encode(title),
meta: generateTagsAsString(TAG_NAMES.META, metaTags),
link: generateTagsAsString(TAG_NAMES.LINK, linkTags)
});
var DocumentMeta = React.createClass({
propTypes: {
title: React.PropTypes.string,
titleTemplate: React.PropTypes.string,
meta: React.PropTypes.arrayOf(React.PropTypes.object),
link: React.PropTypes.arrayOf(React.PropTypes.object),
children: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.array
]),
bodyClasses: React.PropTypes.array,
},
render() {
if (Object.is(React.Children.count(this.props.children), 1)) {
return React.Children.only(this.props.children);
} else if (React.Children.count(this.props.children) > 1) {
return (
<span>
{this.props.children}
</span>
);
}
return null;
},
});
DocumentMeta = withSideEffect(reducePropsToState, handleClientStateChange, mapStateOnServer)(DocumentMeta);
module.exports = DocumentMeta;
This component could probably be changed a little for your case (withSideEffect is used for both client and server side rendering... if you arent using server side rendering then its probably not completely necessary) but the component will work on client side rendering if you would like to use it there as well.
ReactJS doesn't have direct access to the <body> element, and that's the element that needs to have its overflow-y style changed. So while what you're doing isn't perhaps the prettiest code, it's not entirely wrong either.
The only real suggestion I'd give is (shudder) using inline styles on the body instead of a classname so as to avoid having to introduce the CSS declaration. As long as your menu is the only thing responsible for updating the overflow-y attribute, there's no reason you can't use an inline style on it. Mashing that down with the ?: operator results in fairly simple code:
body.style.overflowY = menuOpen ? "hidden" : "";
And then you can just delete the .overflow-hidden class in its entirety.
If for some reason multiple things are managing the overflow state of the body, you might want to stick with classnames and assign a unique one for each thing managing it, something like this:
if (menuOpen) {
body.className += ' menu-open';
}
else {
// Use some tricks from jQuery to remove the "menu-open" class more elegantly.
var className = " " + body.className + " ";
className = className.replace(" overflow-hidden ", " ").replace(/\s+/, " ");
className = className.substr(1, className.length - 2);
}
CSS:
body.menu-open {
overflow-y: hidden;
}

trying to toggle HTML wrap with TinyMCE

I'm looking for hours for a solutions but can't find anything that works for me.. I am trying to create a button which will wrap a specific text with span which works fine.
Now i am trying to make it a toggle so i will be able to unwrap the span from the text but can't get it done..
Basically the button needs to work the same as the Italic button but i couldn't find its code anywhere in my files..
How can i determine if i'm on my requested node so i can deactivate?
How can i make it work as toggle as i mentioned above?
Here is my code-
ed.addButton ('remark', {
title : 'Remark Text',
image: url + '../../images/remark-icon.png',
onClick : function() {
var state = true;
ed.focus();
ed.controlManager.setActive('remark', state);
var text = ed.selection.getContent({format : 'text'});
var selected_elem = ed.selection.getNode(); // Get selected element
var elem_type = ed.selection.getNode().nodeName; // Get element type
selected_elem = jQuery(selected_elem ).attr('class'); // Get element's class
if( elem_type !== 'SPAN') {
ed.selection.setContent('<span class="remark-text">' + text + '</span>');
}
ed.on('NodeChange', function(e) {
state = true;
ed.controlManager.setActive('remark', state);
});
}
});
Thanks for any help!
ed.addButton ('remark', {
title : 'Remark Text',
image: url + '../../images/remark-icon.png',
onClick : function() {
var state = true;
ed.focus();
ed.controlManager.setActive('remark', state);
var text = ed.selection.getContent({format : 'text'});
var selected_elem = ed.selection.getNode(); // Get selected element
var elem_type = ed.selection.getNode().nodeName; // Get element type
selected_elem = jQuery(selected_elem); // Get element's class
console.log(selected_elem);
if( elem_type !== 'SPAN') {
ed.selection.setContent('<span class="remark-text">' + text + '</span>');
}
ed.on('NodeChange', function(e) {
elem_type = ed.selection.getNode().nodeName;
if( elem_type == 'SPAN' && selected_elem.find("span").hasClass("remark-text") ) {
state = true;
ed.controlManager.setActive('remark', state);
}
else {
state = false;
ed.controlManager.setActive('remark', state);
}
});
}
});

Joomla template issues

I'm working on a website for a school project, I'm using a template called quantua. If you go to live demo of the template ( http://www.templatesjoomla.net/index.php/joomla3-x/244-quantua-joomla-3-x.html ), you can see that when resizing the browser window the image shrinks so much that a black background appears. I don't want it to do that, how can the resizing be achived like on this (http://www.websitetemplatesonline.com/free-template/progress-business.html) template? I played with css quite a bit, but i failed, so I guess it has to do with javascript?
heres the javascript code
(function($) {
var NivoSlider = function(element, options){
// Defaults are below
var settings = $.extend({}, $.fn.nivoSlider.defaults, options);
// Useful variables. Play carefully.
var vars = {
currentSlide: 0,
currentImage: '',
totalSlides: 0,
running: false,
paused: false,
stop: false,
controlNavEl: false
};
// Get this slider
var slider = $(element);
slider.data('nivo:vars', vars).addClass('nivoSlider');
// Find our slider children
var kids = slider.children();
kids.each(function() {
var child = $(this);
var link = '';
if(!child.is('img')){
if(child.is('a')){
child.addClass('nivo-imageLink');
link = child;
}
child = child.find('img:first');
}
// Get img width & height
var childWidth = (childWidth === 0) ? child.attr('width') : child.width(),
childHeight = (childHeight === 0) ? child.attr('height') : child.height();
if(link !== ''){
link.css('display','none');
}
child.css('display','none');
vars.totalSlides++;
});
// If randomStart
if(settings.randomStart){
settings.startSlide = Math.floor(Math.random() * vars.totalSlides);
}
// Set startSlide
if(settings.startSlide > 0){
if(settings.startSlide >= vars.totalSlides) { settings.startSlide = vars.totalSlides - 1; }
vars.currentSlide = settings.startSlide;
}
// Get initial image
if($(kids[vars.currentSlide]).is('img')){
vars.currentImage = $(kids[vars.currentSlide]);
} else {
vars.currentImage = $(kids[vars.currentSlide]).find('img:first');
}
// Show initial link
if($(kids[vars.currentSlide]).is('a')){
$(kids[vars.currentSlide]).css('display','block');
}
// Set first background
var sliderImg = $('<img class="nivo-main-image" src="#" />');
sliderImg.attr('src', vars.currentImage.attr('src')).show();
slider.append(sliderImg);
// Detect Window Resize
$(window).resize(function() {
slider.children('img').width(slider.width());
sliderImg.attr('src', vars.currentImage.attr('src'));
sliderImg.stop().height('auto');
$('.nivo-slice').remove();
$('.nivo-box').remove();
});
//Create caption
slider.append($('<div class="nivo-caption"></div>'));
// Process caption function
var processCaption = function(settings){
var nivoCaption = $('.nivo-caption', slider);
if(vars.currentImage.attr('title') != '' && vars.currentImage.attr('title') != undefined){
var title = vars.currentImage.attr('title');
if(title.substr(0,1) == '#') title = $(title).html();
if(nivoCaption.css('display') == 'block'){
setTimeout(function(){
nivoCaption.html(title);
}, settings.animSpeed);
} else {
nivoCaption.html(title);
nivoCaption.stop().fadeIn(settings.animSpeed);
}
} else {
nivoCaption.stop().fadeOut(settings.animSpeed);
}
}
//Process initial caption
processCaption(settings);
// In the words of Super Mario "let's a go!"
var timer = 0;
if(!settings.manualAdvance && kids.length > 1){
timer = setInterval(function(){ nivoRun(slider, kids, settings, false); }, settings.pauseTime);
}
// Add Direction nav
if(settings.directionNav){
slider.append('<div class="nivo-directionNav"><a class="nivo-prevNav">'+ settings.prevText +'</a><a class="nivo-nextNav">'+ settings.nextText +'</a></div>');
// Hide Direction nav
if(settings.directionNavHide){
$('.nivo-directionNav', slider).hide();
slider.hover(function(){
$('.nivo-directionNav', slider).show();
}, function(){
$('.nivo-directionNav', slider).hide();
});
}
$('a.nivo-prevNav', slider).live('click', function(){
if(vars.running) { return false; }
clearInterval(timer);
timer = '';
vars.currentSlide -= 2;
nivoRun(slider, kids, settings, 'prev');
});
$('a.nivo-nextNav', slider).live('click', function(){
if(vars.running) { return false; }
clearInterval(timer);
timer = '';
nivoRun(slider, kids, settings, 'next');
});
}
// Add Control nav
if(settings.controlNav){
vars.controlNavEl = $('<div class="nivo-controlNav"></div>');
slider.after(vars.controlNavEl);
for(var i = 0; i < kids.length; i++){
if(settings.controlNavThumbs){
vars.controlNavEl.addClass('nivo-thumbs-enabled');
var child = kids.eq(i);
if(!child.is('img')){
child = child.find('img:first');
}
if(child.attr('data-thumb')) vars.controlNavEl.append('<a class="nivo-control" rel="'+ i +'"><img src="'+ child.attr('data-thumb') +'" alt="" /></a>');
} else {
vars.controlNavEl.append('<a class="nivo-control" rel="'+ i +'">'+ (i + 1) +'</a>');
}
}
//Set initial active link
$('a:eq('+ vars.currentSlide +')', vars.controlNavEl).addClass('active');
$('a', vars.controlNavEl).bind('click', function(){
if(vars.running) return false;
if($(this).hasClass('active')) return false;
clearInterval(timer);
timer = '';
sliderImg.attr('src', vars.currentImage.attr('src'));
vars.currentSlide = $(this).attr('rel') - 1;
nivoRun(slider, kids, settings, 'control');
});
}
//For pauseOnHover setting
if(settings.pauseOnHover){
slider.hover(function(){
vars.paused = true;
clearInterval(timer);
timer = '';
}, function(){
vars.paused = false;
// Restart the timer
if(timer === '' && !settings.manualAdvance){
timer = setInterval(function(){ nivoRun(slider, kids, settings, false); }, settings.pauseTime);
}
});
}
// Event when Animation finishes
slider.bind('nivo:animFinished', function(){
sliderImg.attr('src', vars.currentImage.attr('src'));
vars.running = false;
// Hide child links
$(kids).each(function(){
if($(this).is('a')){
$(this).css('display','none');
}
});
// Show current link
if($(kids[vars.currentSlide]).is('a')){
$(kids[vars.currentSlide]).css('display','block');
}
// Restart the timer
if(timer === '' && !vars.paused && !settings.manualAdvance){
timer = setInterval(function(){ nivoRun(slider, kids, settings, false); }, settings.pauseTime);
}
// Trigger the afterChange callback
settings.afterChange.call(this);
});
You are looking in the wrong place. use firebug to inspect and know which css file is being used. the file is default.css. Line 159. Thats what you should be playing with. this is what it looks like
.nivoSlider {
float: left;
position: relative;
width: 100%;
height: 480px;
overflow: hidden;
background: #000
this is the block of code that affects the presentation of the navigation bar. but to eradicate the blackness down there, you can use this close fix
change the property height of the nivoslider class to auto on line 159
change the backgroud property of the navigation class from transparent to black on line 296
change the margin-top property of the id menu class to 316px on line 285
this is not guaranteed to not distort especially the last point. but keep trying.

Make fixed positioned div relatively to another div,

I started the first question here: but it was very hard to make the effect for each single page I had, So I thought if I made the div that I wanted to make fixed not fixed to the screen scroll
Very Hard to compute heights with pure math and it doesn't work ultimately as you need to modify the calculation for every single page
Here is the code :
<script >window.addEventListener("scroll",function() {
if(window.scrollY >= 148) {
document.getElementById('main_nav').style.position = 'absolute';
document.getElementById('main_nav').style.bottom = '65%';
}
if(window.scrollY <= 148){
document.getElementById('main_nav').style.position = 'fixed';
document.getElementById('main_nav').style.top = '42%';
}
});</script>
so to get things clear That piece of code depends on scroll height of screen.
I need to apply the following effect :
when the page loads the main_nav div is position:fixed.
On scrolling down
when main_nav is above some div by: say 20px it should stop position:fixed.
It should stand still at it's last place.
On scrolling up again.
It should restore the position fixed again .
"This creates the float effect till some point"
Someone I know gave me the link for this question and a js code and told me to past it here
so here its if it is not relevant to the subject please let me knw
Someone I know gave me the link for this question and a js code and told me to past it here
so here its if it is not relevant to the subject please let me know and I will delete it
/* ===========================================================
* fixate.js v1
* Use:
* Inspired by the original jquery sticky scroller
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* FIXATE PUBLIC CLASS DEFINITION
* =============================== */
var Fixate = function (element, options) {
this.init(element, options);
}
Fixate.prototype = {
constructor: Fixate
, init: function (element, options) {
var self, opts;
this.$element = $(element);
opts = this.options = this.getOptions(options);
this.zindex = this.$element.css('z-index');
this.$window = $(window);
this.$doc = $(document);
this.$bottomCap = opts.bottomCapEl ? $(opts.bottomCapEl) : false;
this.$topCap = opts.topCapEl ? $(opts.topCapEl) : false;
this.bottomOffset = opts.windowInset.bottom || 0;
this.topOffset = opts.windowInset.top || 0;
this.top = this.$element.offset().top - parseFloat(this.$element.css('margin-top').replace(/auto/, 0)) - this.topOffset;
this.eto = this.$element.offset().top;
this.origTop = this.$element.css('top');
this.origBottom = this.$element.css('bottom');
this.z = (this.zindex === '0' || this.zindex === 'auto') ? opts.zindex : this.zindex;
this.bh = (this.$bottomCap) ? this.$bottomCap.outerHeight(true) : 0;
self = this;
this.$window.on('scroll', function (e) {
self.fixate();
});
this.$doc.on('DOMNodeInserted DOMNodeRemoved', function(e){
//Called when elements are added or removed from DOM
self.fixate();
});
}
, getOptions: function (options) {
options = $.extend({}, $.fn['fixate'].defaults, options, this.$element.data());
return options;
}
, fixate: function(){
var s = this.$window.scrollTop()
, h = this.$element.outerHeight(true);
// Calc offset onscroll to get most updated results - incasse ajaxed els come in
this.bco = (this.$bottomCap) ? this.$bottomCap.offset().top : 0;
this.tco = (this.$topCap) ? this.$topCap.offset().top + this.$topCap.outerHeight(true) : 0;
this.dh = this.$doc.height();
if(this.options.windowEdge === 'top'){
this.fixToTop(s, h);
} else {
this.fixToBottom(s, h);
}
}
, fixToTop: function (s, h) {
var bco = this.bco
, to = this.topOffset
, eto = this.eto
, bo = this.bottomOffset
, point = bco - h - bo - to;
if (s >= this.top) {
this.$element.css({
'position': 'fixed',
'top': to,
'z-index': this.z
});
this.fixTouchDevice();
// Bottom cap calc -check cpu factor
if (s >= point) {
this.$element.css({
'top': Math.round(point - this.top + parseFloat(this.origTop)),
'position': 'absolute'
});
}
} else {
this.$element.css({
'position': '',
'top': this.origTop,
'z-index': this.zindex
});
}
}
, fixToBottom: function (s, h) {
var bco = this.bco
, to = this.topOffset
, eto = this.eto
, bo = this.bottomOffset;
if (s >= ( bco - h - bo - to - this.top) ) {
this.$element.css({
'bottom': Math.round(this.dh-(bco - bo)),
'position': 'absolute'
});
this.fixTouchDevice();
} else {
this.$element.css({
'position': 'fixed',
'bottom': bo,
'z-index': this.z
});
}
}
, fixTouchDevice: function(){
//stick the footer at the bottom of the page if we're on an iPad/iPhone due to viewport/page bugs in mobile webkit
if(navigator.platform == 'iPad' || navigator.platform == 'iPhone' || navigator.platform == 'iPod') {
this.$element.css("position", "static");
}
}
}
/* FIXATE PLUGIN DEFINITION
* ========================= */
$.fn.fixate = function (option) {
return this.each(function () {
var $this = $(this),
data = $this.data('fixate'),
options = typeof option == 'object' && option;
if (!data) $this.data('fixate', (data = new Fixate(this, options)));
if (typeof option == 'string') data[option]();
})
}
$.fn.fixate.Constructor = Fixate;
$.fn.fixate.defaults = {
windowInset: {
top: 0,
bottom: 0
}
, windowEdge: 'top'
, bottomCapEl: 'footer'
, topCapEl: 'header'
, zindex: 5
};
}(window.jQuery);

Phantomjs doesn't render footers with a custom styles

I have the following example:
var page = require('webpage').create(),
system = require('system');
if (system.args.length < 3) {
console.log('Usage: printheaderfooter.js URL filename');
phantom.exit(1);
} else {
var address = system.args[1];
var output = system.args[2];
page.viewportSize = { width: 600, height: 600 };
page.paperSize = {
format: 'A4',
margin: "1cm"
footer: {
height: "1cm",
contents: phantom.callback(function(pageNum, numPages) {
if (pageNum == numPages) {
return "";
}
return "<h1 class='footer_style'>Footer" + pageNum + " / " + numPages + "</h1>";
})
}
};
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
} else {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
});
}
In the example above I use footer_style class that look likes in my css file the following:
.footer_style {
text-align:right;
}
But unfortunately that dosen't works. I'm trying to create pdf file such as follows:
./phantomjs rasterize.js index.html test.pdf
We know that classes do not work but inline styles do. What we can do is replace the class with the computed style.
Here is a function that will take a piece of html, create a temporary element in the body with the html, compute the style for each element with a class, add the computed style inline and return the new html.
function replaceClassWithStyle(html) {
return page.evaluate(function(html) {
var host = document.createElement('div');
host.innerHTML = html;
document.body.appendChild(host); // if not appended, values will be blank
var elements = host.getElementsByTagName('*');
for (var i in elements) {
if (elements[i].className) {
elements[i].setAttribute('style', window.getComputedStyle(elements[i], null).cssText);
}
}
document.body.removeChild(host);
return host.innerHTML;
}, html);
}
Then simply call this function in your footer:
page.paperSize = {
footer: {
contents: phantom.callback(function(pageNum, numPages) {
if (pageNum == numPages) {
return "";
}
return replaceClassWithStyle("<h1 class='footer_style'>Footer" + pageNum + " / " + numPages + "</h1>");
})
}
};
You will need to move all this inside page.open().
I tested it and the footer is aligned to the right.
I have an update to mak's excellent answer for PhantomJS 1.9.7.
This version fixes:
Circumvent bug which 'blank's the parent document (PhantomJS 1.9.7)
Style mixups when styles are nested (do depth-first traversal instead)
Also works when tags do not have classes
/**
* Place HTML in the parent document, convert CSS styles to fixed computed style declarations, and return HTML.
* (required for headers/footers, which exist outside of the HTML document, and have trouble getting styling otherwise)
*/
function replaceCssWithComputedStyle(html) {
return page.evaluate(function(html) {
var host = document.createElement('div');
host.setAttribute('style', 'display:none;'); // Silly hack, or PhantomJS will 'blank' the main document for some reason
host.innerHTML = html;
// Append to get styling of parent page
document.body.appendChild(host);
var elements = host.getElementsByTagName('*');
// Iterate in reverse order (depth first) so that styles do not impact eachother
for (var i = elements.length - 1; i >= 0; i--) {
elements[i].setAttribute('style', window.getComputedStyle(elements[i], null).cssText);
}
// Remove from parent page again, so we're clean
document.body.removeChild(host);
return host.innerHTML;
}, html);
}
From my past experience, phantomjs does not support styles in custom header/footer.
The only solution that I found is to apply an inline style like this :
var page = require('webpage').create(),
system = require('system');
if (system.args.length < 3) {
console.log('Usage: printheaderfooter.js URL filename');
phantom.exit(1);
} else {
var address = system.args[1];
var output = system.args[2];
page.viewportSize = { width: 600, height: 600 };
page.paperSize = {
format: 'A4',
margin: "1cm",
footer: {
height: "1cm",
contents: phantom.callback(function(pageNum, numPages) {
return "<h1 style='text-align:right'>Footer" + pageNum + " / " + numPages + "</h1>";
})
}
};
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
} else {
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
});
}
Note : A comma is missing in your code after margin: "1cm"

Resources