質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.22%

【多重構造】14層の<span>

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 966

s.k

score 409

SNS型求人広告サイトを作っていて躓きました。

前提・実現したいこと

多重<span>構造を取り除き、「もっと読む」機能を実装したい。

発生している問題・エラーメッセージ

<span>が繰り返し、出力されます。

【該当箇所の出力スタート地点(青い部分)】
イメージ説明

【該当箇所の出力終末地点(青い部分)】
イメージ説明

ブラウザ画面ではこのようになっています。
この多重<span>構造が「もっと読む」機能の実装を妨げているように思われます。

【moreを押す前】
イメージ説明

【moreを押した後】
イメージ説明

本当はこうなってほしい。。。

【moreを押す前】
イメージ説明

【moreを押した後】
イメージ説明

なぜ、<span>が繰り返されるのかがわからないのです。
ちなみにタイムライン上に呼び出される_micropost.html.erbは多重<span>構造のエラーが発生しますが、コメント一覧をみるときにmicropostが呼び出されるshow.html.erbでは正常に作用しています。(moreを押すと隠された文字が表示されます。)

該当のソースコード

【_micropost.html.erb】

<li id="micropost-<%= micropost.id %>">

       <!-- ユーザーアイコン -->
      <% if micropost.shop.nil? %>
       <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
      <% elsif micropost.user.nil? %>
       <%= link_to gravatar_to(micropost.shop, size: 50), micropost.shop %>
      <% end %>

      <!-- ユーザー名 -->
      <% if micropost.shop.nil? %>
      <!-- User名 -->
      <span class="user"><%= link_to micropost.user.username, micropost.user %></span>
      <% elsif micropost.user.nil? %>
      <!-- Shop名 -->
      <span class="user"><%= link_to micropost.shop.shopname, micropost.shop %></span>
      <% end %>

      <!-- User/Shop 共通 -->
      <span class="content">
        <!-- 投稿文 -->
        <div class="more">
          <%= micropost.content %>←ここが問題箇所
        </div>
        <!-- 投稿写真 -->
        <%= image_tag micropost.picture.url if micropost.picture? %>
      </span>
      <!-- 投稿時間 -->
      <span class="timestamp">
        Posted <%= time_ago_in_words(micropost.created_at) %> ago.
      </span>


      <% if user_signed_in? %>
          <!-- 削除リンク User ver-->
          <% if current_user?(micropost.user) %>
              <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
          <% end %>

          <!-- お気に入り登録リンク user-to-micropost.user-->
      <% if micropost.shop.nil? %>
            <% if !current_user?(micropost.user) %>
                <%= render 'evaluates/evaluate_links', micropost: micropost %>
            <% end %>

      <% elsif micropost.user.nil? %>
          <!-- いいね!リンク user-to-micropost.shop -->
              <% if !current_user?(micropost.user) %>
                  <%= render 'praises/praise_links', micropost: micropost %>
              <% end %>
      <% end %>



      <% elsif shop_signed_in? %>
          <!-- 削除リンク Shop ver-->
          <% if current_shop?(micropost.shop) %>
              <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
          <% end %>

        <% if micropost.shop.nil? %>
          <!-- お気に入り登録リンク shop-to-micropost.user-->
            <% if !current_shop?(micropost.shop) %>
                <%= render 'sympathizes/sympathize_links', micropost: micropost %>
            <% end %>

        <% elsif micropost.user.nil? %>
          <!-- いいね!リンク shop-to-micropost.shop -->
              <% if !current_shop?(micropost.shop) %>
                  <%= render 'likes/like_links', micropost: micropost %>
              <% end %>
        <% end %>


      <% end %>

        <%= link_to 'コメントを見る', micropost_path(micropost.id) %>

</li>
<script>
$(document).ready(function() {
    var showChar = 25;
    var ellipsestext = "...";
    var moretext = "more";
    var lesstext = "less";
    $('.more').each(function() {
        var content = $.trim($(this).html());

        if(content.length > showChar) {

            var c = content.substr(0, showChar);
            var h = content.substr(showChar, content.length - showChar);

            var html = c + '<span class="moreellipses">' + ellipsestext+ '&nbsp;</span><span class="morecontent"><span>' + h + '</span>&nbsp;&nbsp;<a href="" class="morelink">' + moretext + '</a></span>';

            $(this).html(html);
        }

    });

    $(".morelink").click(function(){
        if($(this).hasClass("less")) {
            $(this).removeClass("less");
            $(this).html(moretext);
        } else {
            $(this).addClass("less");
            $(this).html(lesstext);
        }
        $(this).parent().prev().toggle();
        $(this).prev().toggle();
        return false;
    });
});
</script>

【_feed.html.erb】ここのパーシャルはエラーになる

<% if @feed_items.any? %>
 <ol class="microposts">
     <%= render @feed_items %> ←ここで_micropost.html.erbを呼び出します。
 </ol>
 <%= will_paginate @feed_items %>
<% end %>

【show.html.erb】→「more」を押すと、隠された文字が表示されます。

       <div class="row">
        <aside class="col-md-4">
            <!-- 投稿 -->
            <div class="col-md-8">
            <%= render @micropost, micropost: @micropost %> ←ここで_micropost.html.erbを呼び出している。
            </div>




            <!-- コメント -->
            <div class="col-md-8">

                            <!-- user-to-micropost.user -->

                                    <% @comments. each do |comment| %>
                                     <div>
                                         <strong><%= comment.user.username %></strong>
                                         </br>
                                         <p><%= comment.body %></p>
                                         <% if user_signed_in? && comment.user == current_user %>
                                          <p><%= link_to 'Delete', comment_path(comment), method: :delete %></p>
                                         <% end %>
                                     </div>
                                    <% end %>

                            <!-- user-to-micropost.shop -->

                                <% @calls. each do |call| %>
                                 <div>
                                     <strong><%= call.user.username %></strong>
                                     </br>
                                     <p><%= call.body %></p>
                                     <% if user_signed_in? && call.user == current_user %>
                                      <p><%= link_to 'Delete', call_path(call), method: :delete %></p>
                                     <% end %>
                                 </div>
                                <% end %>
                            <% if user_signed_in? %>
                              <%= render 'calls/form' %>
                            <% end %>
                            <!-- shop-to-micropost.shop -->
                                            <% @says. each do |say| %>
                                             <div>
                                                 <strong><%= say.shop.shopname %></strong>
                                                 </br>
                                                 <p><%= say.body %></p>
                                                 <% if shop_signed_in? && say.shop == current_shop %>
                                                  <p><%= link_to 'Delete', say_path(say), method: :delete %></p>
                                                 <% end %>
                                             </div>
                                            <% end %>
                                            <% if shop_signed_in? %>
                                                <%= render 'says/form' %>
                                            <% end %>

                            <!-- shop-to-micropost.shop -->

                                            <% @insists. each do |insist| %>
                                             <div>
                                                 <strong><%= insist.shop.shopname %></strong>
                                                 </br>
                                                 <p><%= insist.body %></p>
                                                 <% if shop_signed_in? && insist.shop == current_shop %>
                                                  <p><%= link_to 'Delete', insist_path(insist), method: :delete %></p>
                                                 <% end %>
                                             </div>
                                            <% end %>
                                            <% if shop_signed_in? %>
                                                <%= render 'insists/form' %>
                                            <% end %>
            </div>
        </aside>
       </div>

試したこと

・html5.validator → 500 Internal Server Error
・css内で.moreが重複されていないか確認済み
・show,_feed,_micropost.html.erb内のdivを一つ削除しながらmoreが動くか確認済み

補足情報(言語/FW/ツール等のバージョンなど)

下記URL内の質問が発端です。
より詳細な情報が載っております。

投稿文の開閉設定の変数がうまいこと作用しない。

開閉設定の参考コード
[もっとみる...] / [See more...] をjQueryで実装する(開閉できるタイプ)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

$('.more').each(function() {
        var content = $.trim($(this).html());

        if(content.length > showChar) {

            var c = content.substr(0, showChar);
            var h = content.substr(showChar, content.length - showChar);

            var html = c + '<span class="moreellipses">' + ellipsestext+ '&nbsp;</span><span class="morecontent"><span>' + h + '</span>&nbsp;&nbsp;<a href="" class="morelink">' + moretext + '</a></span>';

            $(this).html(html);
        }

    });

このコードが悪いです。

viewのjavascriptからviewを動的domを初期化するのは基本的にアンチパターンです。
ajaxとform_builderを使うようにしましょう。
またjavascriptはviewに書かずcoffeescriptなどを利用した方がいいと思います。モデルが関連したUIにjavascriptを使うとrailsとロジックを接続する必要が生じるため二度手間になります。基本的にUIのアニメーションやデザインのみにjsを使う方がいいと思います。

またviewファイルの整理はもっと頑張った方がいい気がします。現状読みたくないコードになっています。基本的にロジックはhelperに切り出しviewは最低限のロジックで済ませましょう。formはformbuilderとモデルの関連ごとにpartial_templateを使って実装したほうが問題が部分的に切り出しやすくなります。そうすればviewファイル全てを載せなくても部分的にコードを載せられるので質問もしやすく回答する側も答えやすくなります。さらに言うと質問前にバグに気付きやすくなります。

viewの実装が汚いことに悩んだ時にはそれを解消するgemも色々あります。

・gon
・cocoon
・reform
・haml
・simple_form

このあたりを調べてみるといいと思います。

また,jsメインで行きたいなら,railsのformbuilderを捨ててjsのライブラリやフレームワークに実装を委ねてしまった方がメンテしやすくなります。railsはreactやvueと親和性が高いので,jsに懲りたい場合はこちらを利用することをお勧めします。

多少まさかり投げさせていただきましたが,railsのデザインパターンをもう少し意識してコードを書いてもらえると開発時間も短くなり,バグも減りいいプロダクトを作れるようになると思うので頑張ってください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/01 11:11


    tkowさん

    ありがとうございます!
    昨日から、悩んでいましたので助かりました…

    アンチパターン気を付けます…
    とりあえず、試験的にあのjs機能を試してみたかったのでこのようなコードにしてしまいました。

    「viewに直接かくのはよくはないけど、書いてもエラーにはならない」と勘違いしていました。

    このjsをajaxにするのは大変そうですね(ajaxは簡単なものしかできないので)
    でも、やってみます!

    form_builderですが、これは今から調べます。

    コードの汚さは申し訳ないです(´;ω;`)
    整理せず走り書きしてきましたので、こんな状態です。

    tkowさんにいただいた情報をもとに
    整理してみます!

    キャンセル

  • 2016/11/01 16:34 編集

    jsの動作確認したい時はchromeのsnippets使うといいですよ(他のブラウザにもあるかも)。
    書いたコードをrunで実行できて保存できます。macだとcmd+o ->ファイル選択->cmd+Enterで実行できて楽です。
    ブックマークレットでもできますがappendでsrc属性を書いてjsライブラリやcssロードもできます。

    >ajaxは簡単なものしかできないので

    実はajaxは難しそうに見えて,form builderで作ったフォームであればformにremote=true属性とpartial templateを送信するjsを作るだけで出来てしまいます。気をつけるのはform丸々変更する場合security_tokenまで差し替えてしまったり逆にワンタイムトークンの時はform毎書き換えないとerror吐くのに注意するくらいでしょうか。後者の場合formが複数存在してしまう場合は諦めてredirectで対応したりします。
    頑張ってください。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる