How can I arrange individual events in agendaWeek view on FullCalendar.io and then merge them all into one line?

I am using Fullcalendar.io version 2

When I switch to the agendaWeek mode, all my events are displayed on one line in each day square. As a result, the more events I have, the thinner the event blocks become.

Is there a way for me to display only one event per line, similar to how it is shown in the month mode? Additionally, can the height of the day block increase with more events? It seems challenging to achieve this using functions like eventRender, as examining the .fs-event element reveals that the event blocks use properties such as

position:absolute;top:300px;left:33%
...

https://i.sstatic.net/rrwtf.png

I aim for a layout like this: https://i.sstatic.net/v9zq8.png

Answer №1

I encountered the same issue with that particular problem, which proved to be quite challenging due to the unconventional way the plugin constructs the calendar using tables and dynamically positioning events with absolute values for the top CSS property.

Fortunately, I was able to devise a versatile solution that functions admirably well. Firstly, I will present you with the code, followed by an explanation of its operation.

To address this issue, I utilized the eventAfterAllRender option in FullCalendar. Here is a live example.https://i.sstatic.net/ZoC5e.png

I made use of moment for time management and assumed that the ID of the FullCalendar HTML element is 'Calendar'.

eventAfterAllRender: function() {

    // define static values, use these values to adjust event item height
    var defaultItemHeight = 25;
    var defaultEventItemHeight = 18;
    // ...

    // retrieve all rows and create a function to select a row based on a specific time
    var rows = [];
    $('div.fc-slats > table > tbody > tr[data-time]').each(function() {
      rows.push($(this));
    });
    var rowIndex = 0;
    var getRowElement = function(time) {
      while (rowIndex < rows.length && moment(rows[rowIndex].attr('data-time'), ['HH:mm:ss']) <= time) {
        rowIndex++;
      }
      var selectedIndex = rowIndex - 1;
      return selectedIndex >= 0 ? rows[selectedIndex] : null;
    };

    // reposition events items and increase row height when necessary
    $('div.fc-content-col > div.fc-event-container').each(function() {  // iterate through week columns
      var accumulator = 0;
      var previousRowElement = null;

      $(this).find('> a.fc-time-grid-event.fc-v-event.fc-event.fc-start.fc-end').each(function() {  // iterate through events in column
        // determine the current event time and its respective row
        var currentEventTime = moment($(this).find('> div.fc-content > div.fc-time').attr('data-full'), ['h:mm A']);
        var currentEventRowElement = getRowElement(currentEventTime);

        // move down events if multiple in same row (using margin-top to avoid conflicts)
        if (currentEventRowElement === previousRowElement) {
          accumulator++;

          $(this).css('margin-top', '+=' + (accumulator * defaultItemHeight).toString() + 'px');

          var maxItemsOnRow = currentEventRowElement.attr('data-max-items') || 1;
          if (accumulator >= maxItemsOnRow) {
            currentEventRowElement.attr('data-max-items', accumulator + 1);
            currentEventRowElement.css('height', '+=' + defaultItemHeight.toString() + 'px');
          }
        } else {
          rowIndex = 0;
          accumulator = 0;
        }

        // set default styles for event item and update previosRow
        $(this).css('left', '0');
        $(this).css('right', '7px');
        $(this).css('height', defaultEventItemHeight.toString() + 'px');
        $(this).css('margin-right', '0');
        previousRowElement = currentEventRowElement;
      });
    });

    $('#calendar').fullCalendar('option', 'aspectRatio', $('#calendar').fullCalendar('option', 'aspectRatio'));
  }

How Does the Code Operate:

Initially, I located all tr elements representing the rows in my calendar, each containing its own timestamp attribute.

Subsequently, I iterated over each column, retrieving the event items within each one represented by anchor elements with date attributes such as data-full.

For each event, I determined its appropriate row position and adjusted it vertically using the margin-top property rather than the standard top property to prevent plugin conflicts.

In addition, I tracked the maximum number of events in a row to ensure proper height adjustments.

This outlines the primary functionality of the code. Feel free to inquire further if needed.

Answer №2

To customize your events, you can add a class and style them using CSS.

For example, you can create a style like this:

.test {
    width: 100%;
    height: auto;
    position: relative !important;
    left: 0% !important;
    margin-right: 0% !important;
}

and then apply the class to an event like this:

{
    title: 'Lunch',
    start: '2014-06-09T10:30:00',
    className: 'test'
},

If this is what you're looking for, check out this Fiddle.

Another option is to use the eventAfterRender callback to adjust the height of a specific row, but this may require some tweaking:

eventAfterRender: function( event, element, view ) { 
    var row = $(".fc-slats tr:contains('"+ moment(event.start).format('ha') + "')");
    if (moment(event.start).format('mm') != '00')
    {
        row = row.next();
    }
    row.height(element.height()+row.height());
}

You can see an example of this workaround here: https://jsfiddle.net/m5uupf9x/3/

Answer №3

I encountered a similar issue and while Alexander's response was helpful, I experienced performance issues due to excessive DOM manipulation. With around 2000-3000 events per week, it became unmanageable in browsers like Firefox and IE. Therefore, by modifying Alexander's solution and reducing DOM manipulation, I devised the following approach.

Variables

var itemsOnSlot = {};       // keeping track of number of events in each time slot
var maxItemsOnRow = {};     // tracking maximum number of events in a row

Utilizing eventRender and eventAfterAllRender callbacks

eventRender: function(event, element, view){
    if(!(event.start in itemsOnSlot)){
        itemsOnSlot[event.start] = 1;
        $(element).addClass('slot-attributes');
    }else{
        itemsOnSlot[event.start] += 1;
        $(element).css('cssText','margin-top:'+(itemsOnSlot[event.start]*18)+'px !important;').addClass('slot-attributes');
    }
},

eventAfterAllRender: function(view) {
    for(var start in itemsOnSlot){
        var time = start.substr(16,8);
        if(!(time in maxItemsOnRow)){
            maxItemsOnRow[time] = itemsOnSlot[start];
        }else{
            if(itemsOnSlot[start] > maxItemsOnRow[time]){
                maxItemsOnRow[time] = itemsOnSlot[start];
            }
        }
    }

    $('div.fc-slats > table > tbody > tr[data-time]').each(function() {
        var time = $(this).attr('data-time');
        if(time in maxItemsOnRow){
            $(this).css('cssText','height:'+(maxItemsOnRow[time]*18)+'px !important;');
        }else{
            $(this).css('cssText','display: none !important;');
        }
    });

    $('#calendar').fullCalendar('option', 'aspectRatio', $('#calendar').fullCalendar('option', 'aspectRatio'));

    itemsOnSlot = {};
    maxItemsOnRow = {};
},

CSS

.slot-attributes {
    left: 4px !important;
    right: 3px !important;
    height: 15px !important;
    margin-right: 0 !important;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountered an undefined error while trying to read promises

I'm attempting to receive a response from a function in order to trigger another function, but I am not receiving the expected response. I encountered the following error message: "TypeError: Cannot read property 'then' of undefined." In my ...

Tips for looping through client.get from the Twitter API with node.js and express

I am in the process of developing an application that can download a specific number of tweets. For this project, I am utilizing node.js and express() within my server.js file. To retrieve data from the Twitter API, I have set up a route app.get('/ap ...

The returned JSON object lacks a specified name

After receiving the JSON data from the server, I noticed an unnamed node within the 'someStuff' object: { "someStuff": { "": { "foo": 0 }, "moreStuff": { "foo": 2 } } } This raises ...

I'm unsure how to utilize the generic type in this particular scenario. It's a bit confusing to me

Recently, I delved into TypeScript generics and applied them in specific scenarios. However, I encountered some challenges. While working with two different interfaces, I faced a need for flexibility. For instance, I needed to make server requests. func ...

How can we efficiently add a new node to a polyline link in gojs while maintaining the original positions of points in the links adjacent to the inserted node

After posting a question on StackOverflow (Seeking Javascript library for displaying and editing networks of nodes and edges), I was directed to the gojs splice sample. The sample has been helpful, but I've hit a roadblock trying to achieve the speci ...

Optimizing Image Loading: Using jQuery and JavaScript to Load Images On Demand

Similar to the functionality on this site: I am interested in implementing a feature that only loads images when they are visible on the screen on my own website. Thank you. If possible, I would also like to add a preloader using jQuery. ...

Can jQuery be used to encrypt AJAX requests for authentication?

As someone who has recently delved into the world of AJAX methodologies, particularly after discovering jQuery not too long ago, I'm curious to learn about the possibility of securely authenticating a user in a PHP environment. Is there a way for jQu ...

Display the outcome of a POST request on the webpage

Currently working with node.js/express and have a view that includes a form. This form POSTs to a route which returns JSON data. I want to be able to submit the form and display the returned data underneath the form on the same view without refreshing the ...

"Utilize Ajax to trigger a custom alert message once data is loaded and ready

Is it possible to customize the data object in order to show a JavaScript alert saying "The email address has already been registered!"? Currently, the servlet returns a boolean indicating whether the email is already in the database. $('#emailInput ...

Utilizing the power of THREE.ShaderLib.phong while integrating subsurface scattering within ThreeJS

My mesh utilizes a ShaderMaterial with THREE.ShaderLib.phong uniforms. I have successfully linked the map, bump, and specular maps textures. The code snippet below demonstrates this: defines = {}; defines["USE_MAP"] = ""; defines["USE_BUMPMAP"] = ""; defi ...

personalized link when uploading images in Jodit Editor

I recently integrated the Jodit Editor (react) with the Insert Image option, allowing users to upload images that are saved in the default location set by the Editor. Now I am curious about how to use a custom URL to insert an image in the editor. Here i ...

An effective method for linking a value from one ng-model to another is by identifying and matching a specific string

I have been tasked with binding a value to one model based on the content of another model when it contains a string that starts with "https". For instance, there are two text fields each associated with a different model. <input type="text" ng-model= ...

Is Jquery getting imported correctly, but AJAX is failing to work?

I am currently working on a Chrome extension that automatically logs in to the wifi network. I have implemented AJAX for the post request, but when I inspect the network activity of the popup, I do not see any POST requests being sent. Instead, it only sho ...

The middleware code remains dormant and is left untouched

I am encountering an issue with this code that is supposed to create a folder if it doesn't already exist. When I debug and set a breakpoint on fs.mkdir, the code does not enter into it. Do you have any idea what could be causing this problem? ... ap ...

Is it possible to adjust table rows to match the height of the tallest row in the table?

I've been attempting to ensure that all table rows have the same height as the tallest element, without using a fixed value. Setting the height to auto results in each row having different heights, and specifying a fixed value is not ideal because the ...

Is it possible to break a single word when word wrapping?

Is it possible to apply word-wrap: break-word to just one word within an element without affecting the entire element? I find that when I apply it to the entire element, it looks messy. I tried searching for a solution but couldn't find anything. Has ...

Use JavaScript to dynamically populate dropdown list options with array elements

I attempted to populate a dropdown list with array elements using javascript, but I encountered issues. I referred to the following links for assistance: JavaScript - populate drop down list with array use a javascript array to fill up a drop down se ...

Encountering a TypeError: relativeURL.replace is not a valid function in Next.js

Why am I encountering this error in my Next.js application? TypeError: relativeURL.replace is not a function. (In 'relativeURL.replace(/^/+/, '')', 'relativeURL.replace' is undefined) Request.js file const API_KEY = process ...

I continue to encounter an error every time I attempt to place an HTML nested div on a separate line

When I structure the HTML like this, I don't encounter any errors: <div class="game-card"><div class="flipped"></div></div> However, if I format it differently, I receive an error message saying - Cannot set property 'vi ...

Implement a delay for a specific function and try again if the delay expires

In my TypeScript code, I am utilizing two fetch calls - one to retrieve an access token and the other to make the actual API call. I am looking to implement a 1-second timeout for the second API call. In the event of a timeout, a retry should be attempted ...