Concurrent Openlayers maps in Rails - A Viable Option

I've been playing around with Openlayers maps on my website. The map functionality is working perfectly, but I'm having trouble getting the maps to display correctly on my page.

My goal is to show a map in each search result on my screen - one map per result. Everything looks good when I just have a basic bootstrap card for each result (you can see an example of this in the first screenshot below). However, as soon as I add the map code, everything starts overlapping (as shown in the second screenshot).

Any suggestions or ideas on how to solve this issue would be greatly appreciated! Thanks!

Screenshot showing results without the map code

Screenshot showing results with the map code

      <% @locations.each do |location| %>
        
            <div class="card" style="width: 18rem;">
                  <div id="map" class="map">
                  <script type="text/javascript">
                    var map = new ol.Map({
                      target: 'map',
                      layers: [
                        new ol.layer.Tile({
                          source: new ol.source.OSM()
                        })
                      ],
                          view: new ol.View({
                            center: ol.proj.fromLonLat([-0.479642,52.641882]),
                            zoom: 10
                          })
                    });
                  </script>
                  
              <div class="card-body">
                <h5 class="card-title">Card title</h5>
                <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
                <a href="#" class="btn btn-primary">Go somewhere</a>
              </div>
            </div>

            </br></br>
      <% end %>

Answer №1

That perspective is like untangled spaghetti. You've overlooked two closing </div> tags and are generating multiple elements with identical ID attributes, resulting in an invalid document structure and triggering sloppy mode parsing. My suggestion is to utilize an HTML validator and segregate content, behavior, and presentation, making it easier to produce high-quality code.

Having multiple elements with the same id attribute means that document.getElementById and document.querySelector usually fetch the first element in the DOM, although this practice is not officially standardized and might yield different outcomes across browsers since it's actually prohibited.

<% @locations.each do |location| %>
  <div class="card">
    <div class="map"></div>
    <div class="card-body">
      <h5 class="card-title">Card title</h5>
      <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
      <a href="#" class="btn btn-primary">Go somewhere</a>
    </div>
  </div>
<% end %>

In cases involving OpenLayers (or Google Maps or Leaflet), there's no actual necessity to use ids for the target map element - such practices are primarily used for creating simple introductory samples, which may not be ideal when aiming to add several maps to a page due to unnecessary complications involved in assigning unique ids and passing them to JavaScript. Instead, any HTMLElement object can be passed.

Let's now delve into some more sophisticated JavaScript:

// insert this into your packs (Rails 6) or assets pipeline (older versions) 
// if you're not utilizing turbolinks, use 'DOMContentLoaded'
document.addEventListener('turbolinks:load', function(){
  let elements = document.querySelectorAll('.card .map');
  let maps = elements.forEach(function(element){
    new ol.Map({
      target: element,
      layers: [
        new ol.layer.Tile({
          source: new ol.source.OSM()
        })
      ],
      view: new ol.View({
        center: ol.proj.fromLonLat([-0.479642,52.641882]),
        zoom: 10
      })
    });
  });
});

This isn't overly complex. We select the elements during turbolinks' page replacement process (and upon initial page load) and then initialize a map instance for each element matching the selector. This approach eliminates redundant calls of the same JavaScript for every loop iteration as seen in those cumbersome script tags.

If you need to pass data like marker positions from Rails to your JavaScript, consider using data attributes.

<% @locations.each do |location| %>
  <div class="card">
     <%= content_tag :div,
         class: 'map',
         data: {
           lat: location.latitude,
           lon: location.longitude
         }
     %> 
     <div class="card-body">
       <h5 class="card-title">Card title</h5>
       <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
       <a href="#" class="btn btn-primary">Go somewhere</a>
    </div>
  </div>
<% end %>
document.addEventListener('turbolinks:load', function(){
  let elements = document.querySelectorAll('.card .map');
  let maps = elements.forEach(function(element){
    new ol.Map({
      target: element,
      layers: [
        new ol.layer.Tile({
          source: new ol.source.OSM()
        })
      ],
      view: new ol.View({
        // Map now centers on right location. Magic!
        center: ol.proj.fromLonLat([data.lon, data.lat]),
        zoom: 10
      })
    });
  });
});

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

Something seems off with my code. It's causing a barrage of errors to appear on the browser

JavaScript is a whole new world for me, and I'm struggling with all these errors. The goal here is simple: click the "generate" button, and under "Most Recent Entry," display the current temperature for the entered zip code, today's date, and th ...

Is it possible to embed HTML code within JavaScript strings?

Is my approach to inserting HTML into a JavaScript string correct? In particular, I want to insert an HTML link into a div tag using JavaScript. Here is the HTML: <div id="mydivtag"></div> And here is the JavaScript code: document.g ...

Converting dates in Javascript

I have retrieved a date/time value of "20131218" from an API response. My goal is to transform this date into the format "2013-12-18". In PHP, this can be easily achieved with the following code: echo date("Y-m-d", strtotime('20131218')); The ...

Guide on implementing a bootstrap loading spinner during the process of retrieving data from an external api

Is there a way to use the bootstrap load spinner to mask the delayed information retrieval from a url and enhance the website's interactivity? ...

What is the proper way to invoke render functions using Vue 3 composition API?

During my time with Vue 2, I would typically call render() in this manner: export default { mounted(){ ... }, render(){ ... }, methods(){ ... } } Now that I'm exploring Vue 3 and the composition API, I ...

Creating duplicates of a parent mesh in THREE.js with its children and additional materials

I am inquiring about a cloning issue I am having with a mesh that has a parent and 4 children, each with different materials. When I clone the parent mesh using this code: let result = cloudObjects.sideCloudGeometry[texture].clone(); The cloned mesh look ...

Using reduce() to group items in an array based on a specific object property

Creating a new array grouped by the property 'desc' of the objects within an existing array is my current task. Here is how I envision it: const sourceArray = [ { id: 'id1', sourceDesc: 'foo', prop1: 'ignoreme', p ...

Encountering a blank page and error message on Vue JS while attempting to generate a JSON file within the

Recently, I encountered a peculiar problem while working on my Vue project using Vue UI in Visual Studio. Before connecting my API, I wanted to prototype and experiment with fake data. So, I decided to create a JSON file in the assets folder to store my m ...

Unraveling unicode escape sequences in JavaScript strings

My code includes a string like \uC88B\uC544\uC694. When I use this string in a node repl (v7.4.0), it displays '좋아요' correctly. However, when I try to use the same string in the following code snippet, it does not work as ex ...

Is it possible to showcase dates within input text boxes prior to POSTing the form?

I am currently working on a form that posts to a PHP script by selecting a date range. For a better understanding, you can check out my visual representation here. Before the data is posted, I would like to display the selected dates in empty boxes or inpu ...

Exploring the art of JSON interpretation within Highcharts

Below is an example snippet that utilizes static data: Highcharts.chart("container", { title: { text: "Highcharts pie chart" }, xAxis: { categories: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Ju ...

HTMLMediaElement does not have the setSinkId method

I am currently in the process of developing a WebRTC application using Angular, with the goal of managing audio output through the setSinkId() method within HTMLMediaElement. However, when attempting to use this method, I am encountering an error message s ...

Incorporating external function from script into Vue component

Currently, I am attempting to retrieve an external JavaScript file that contains a useful helper function which I want to implement in my Vue component. My goal is to make use of resources like and https://www.npmjs.com/package/vue-plugin-load-script. Thi ...

React fails to display the content

Starting my React journey with Webpack configuration. Followed all steps meticulously. No error messages in console or browser, but the desired h1 element is not showing up. Aware that React version is 18, yet working with version 17. App.jsx import Reac ...

Custom directives are designed to receive arrays as string inputs

I've encountered an issue with my custom directive that has an isolated scope. When I pass an Array variable to the directive, it is being treated as a String inside the directive. This is how my directive looks: angular.module('my.directives& ...

JavaScript doesn't seem to be functioning properly within PHP

I have implemented the supersized jQuery script to incorporate sliding backgrounds on my WordPress page. Now, I aim to create unique slides for different sites and require a PHP if request. This is my code: <?php if ( is_page(array('Restaurant&a ...

No content returned by Angular Server

I have implemented a factory in angular 1.6 to make GET requests to a Rails 5 server. The factory contains an $http call like this: $http({method: 'GET', url: urlString, params: dataToSend}) .then(function successCallback(response) { ...

The art of concealing and compressing JavaScript code

Can modern JavaScript obfuscation and minification tools effectively protect my code from reverse engineering? Which obfuscation platforms are the most reliable in thwarting these attempts? Is it possible that a program could easily deobfuscate the code, ...

React-onclickoutside does not function properly within an iframe

I have developed a custom drop-down list using reactjs. I utilized react-onclickoutside to recognize clicks outside the list and close it. While this method works effectively, it does not respond to clicks within an iframe. import onClickOutside from &apo ...

How can you set an input field to be initially read-only and then allow editing upon clicking a button using Vue.js?

//I have two divs that need to be set as readonly initially. When an edit button is clicked, I want to remove the readonly attribute and make them editable. <div> <input type="text" placeholder="<a href="/cdn-cgi/l/email-protection ...